From 5653d6c3976d92d724f1b9ea1fd3a826fbc88986 Mon Sep 17 00:00:00 2001 From: Adin Schmahmann Date: Wed, 28 Aug 2024 13:23:27 -0400 Subject: [PATCH] test: add more complete e2e testing --- cmd/e2e_test.go | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 5 +- go.sum | 7 ++ 3 files changed, 177 insertions(+), 1 deletion(-) diff --git a/cmd/e2e_test.go b/cmd/e2e_test.go index 3b70d87..9459fad 100644 --- a/cmd/e2e_test.go +++ b/cmd/e2e_test.go @@ -2,14 +2,28 @@ package main import ( "context" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" "crypto/sha256" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" "encoding/base64" + "encoding/pem" "errors" "fmt" + libp2pws "github.com/libp2p/go-libp2p/p2p/transport/websocket" + "github.com/multiformats/go-multiaddr" + madns "github.com/multiformats/go-multiaddr-dns" + "log" + "math/big" "net" + "net/http" "os" + "strings" "testing" + "time" "github.com/coredns/caddy" "github.com/coredns/coredns/core/dnsserver" @@ -23,6 +37,11 @@ import ( _ "github.com/coredns/coredns/core/plugin" // Load all managed plugins in github.com/coredns/coredns. _ "github.com/ipshipyard/p2p-forge/acme" _ "github.com/ipshipyard/p2p-forge/ipparser" + + pebbleCA "github.com/letsencrypt/pebble/v2/ca" + pebbleDB "github.com/letsencrypt/pebble/v2/db" + pebbleVA "github.com/letsencrypt/pebble/v2/va" + pebbleWFE "github.com/letsencrypt/pebble/v2/wfe" ) const forge = "libp2p.direct" @@ -332,6 +351,153 @@ func TestIPv6Lookup(t *testing.T) { } } +func TestLibp2pACMEE2E(t *testing.T) { + db := pebbleDB.NewMemoryStore() + logger := log.New(os.Stdout, "", 0) + ca := pebbleCA.New(logger, db, "", 0, 1, 0) + va := pebbleVA.New(logger, 0, 0, false, dnsServerAddress, db) + + wfeImpl := pebbleWFE.New(logger, db, va, ca, false, false, 3, 5) + muxHandler := wfeImpl.Handler() + + acmeHTTPListener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer acmeHTTPListener.Close() + + // Generate the self-signed certificate and private key + certPEM, privPEM, err := generateSelfSignedCert("127.0.0.1") + if err != nil { + log.Fatalf("Failed to generate self-signed certificate: %v", err) + } + + // Load the certificate and key into tls.Certificate + cert, err := tls.X509KeyPair(certPEM, privPEM) + if err != nil { + log.Fatalf("Failed to load key pair: %v", err) + } + + // Create a TLS configuration with the certificate + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + // Wrap the listener with TLS + acmeHTTPListener = tls.NewListener(acmeHTTPListener, tlsConfig) + + go func() { + http.Serve(acmeHTTPListener, muxHandler) + }() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + cas := x509.NewCertPool() + cas.AppendCertsFromPEM(certPEM) + + acmeEndpoint := fmt.Sprintf("https://%s%s", acmeHTTPListener.Addr(), pebbleWFE.DirectoryPath) + certLoaded := make(chan bool, 1) + h, err := client.NewHostWithP2PForge(forge, fmt.Sprintf("http://127.0.0.1:%d", httpPort), acmeEndpoint, "foo@bar.com", cas, func() { + certLoaded <- true + }, true) + if err != nil { + t.Fatal(err) + } + + cp := x509.NewCertPool() + cp.AddCert(ca.GetRootCert(0).Cert) + tlsCfgWithTestCA := &tls.Config{RootCAs: cp} + + localDnsResolver, err := madns.NewResolver(madns.WithDefaultResolver(&net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + d := net.Dialer{ + Timeout: time.Second * 5, // Set a timeout for the connection + } + return d.DialContext(ctx, network, dnsServerAddress) + }, + })) + if err != nil { + t.Fatal(err) + } + customResolver, err := madns.NewResolver(madns.WithDomainResolver("libp2p.direct.", localDnsResolver)) + if err != nil { + t.Fatal(err) + } + + h2, err := libp2p.New(libp2p.Transport(libp2pws.New, libp2pws.WithTLSClientConfig(tlsCfgWithTestCA)), + libp2p.MultiaddrResolver(customResolver)) + if err != nil { + t.Fatal(err) + } + + var dialAddr multiaddr.Multiaddr + hAddrs := h.Addrs() + for _, addr := range hAddrs { + as := addr.String() + if strings.Contains(as, "p2p-circuit") { + continue + } + if strings.Contains(as, "libp2p.direct/ws") { + dialAddr = addr + break + } + } + if dialAddr == nil { + t.Fatalf("no valid wss addresses: %v", hAddrs) + } + + select { + case <-certLoaded: + case <-time.After(time.Second * 30): + t.Fatal("timed out waiting for certificate") + } + + if err := h2.Connect(ctx, peer.AddrInfo{ID: h.ID(), Addrs: []multiaddr.Multiaddr{dialAddr}}); err != nil { + t.Fatal(err) + } +} + +func generateSelfSignedCert(ipAddr string) ([]byte, []byte, error) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, err + } + + serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + if err != nil { + return nil, nil, err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"My Organization"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(365 * 24 * time.Hour), // Valid for 1 year + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IPAddresses: []net.IP{net.ParseIP(ipAddr)}, + } + + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return nil, nil, err + } + + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + privDER, err := x509.MarshalECPrivateKey(priv) + if err != nil { + return nil, nil, err + } + privPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: privDER}) + + return certPEM, privPEM, nil +} + // Input implements the caddy.Input interface and acts as an easy way to use a string as a Corefile. type Input struct { corefile []byte diff --git a/go.mod b/go.mod index 9548365..ec6a7c7 100644 --- a/go.mod +++ b/go.mod @@ -12,11 +12,13 @@ require ( github.com/ipfs/go-ds-badger4 v0.1.5 github.com/ipfs/go-ds-dynamodb v0.1.1 github.com/ipfs/go-log/v2 v2.5.1 + github.com/letsencrypt/pebble/v2 v2.6.0 github.com/libp2p/go-buffer-pool v0.1.0 github.com/libp2p/go-libp2p v0.36.1 github.com/mholt/acmez/v2 v2.0.1 github.com/miekg/dns v1.1.61 github.com/multiformats/go-multiaddr v0.13.0 + github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-varint v0.0.7 google.golang.org/protobuf v1.34.2 @@ -68,6 +70,7 @@ require ( github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -108,6 +111,7 @@ require ( github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/koron/go-ssdp v0.0.4 // indirect + github.com/letsencrypt/challtestsrv v1.3.2 // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect @@ -129,7 +133,6 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect diff --git a/go.sum b/go.sum index 920aa81..eeccb62 100644 --- a/go.sum +++ b/go.sum @@ -151,6 +151,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -316,6 +318,10 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/letsencrypt/challtestsrv v1.3.2 h1:pIDLBCLXR3B1DLmOmkkqg29qVa7DDozBnsOpL9PxmAY= +github.com/letsencrypt/challtestsrv v1.3.2/go.mod h1:Ur4e4FvELUXLGhkMztHOsPIsvGxD/kzSJninOrkM+zc= +github.com/letsencrypt/pebble/v2 v2.6.0 h1:7xetaJ4YaesUnWWeRGSs3UHOwyfX4I4sfOfDrkvnhNw= +github.com/letsencrypt/pebble/v2 v2.6.0/go.mod h1:SID2E75Cx6sQ9AXFkdzhLdQ6S1zhRUbw08Cgu7GJLSk= github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= @@ -355,6 +361,7 @@ github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGo github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=