From 851dab1b27cb27991dea11d4ee28d56cd5fea172 Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Thu, 9 Jan 2025 14:02:13 +0100 Subject: [PATCH 1/6] feat: add dns addr to addrsfactory --- client/acme.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/client/acme.go b/client/acme.go index 5f7d6ca..d894448 100644 --- a/client/acme.go +++ b/client/acme.go @@ -417,7 +417,8 @@ func (m *P2PForgeCertMgr) TLSConfig() *tls.Config { } func (m *P2PForgeCertMgr) AddrStrings() []string { - return []string{fmt.Sprintf("/ip4/0.0.0.0/tcp/0/tls/sni/*.%s/ws", m.forgeDomain), + return []string{ + fmt.Sprintf("/ip4/0.0.0.0/tcp/0/tls/sni/*.%s/ws", m.forgeDomain), fmt.Sprintf("/ip6/::/tcp/0/tls/sni/*.%s/ws", m.forgeDomain), } } @@ -449,7 +450,7 @@ func certName(id peer.ID, suffixDomain string) string { } func (m *P2PForgeCertMgr) createAddrsFactory(allowPrivateForgeAddrs bool) config.AddrsFactory { - var p2pForgeWssComponent = multiaddr.StringCast(fmt.Sprintf("/tls/sni/*.%s/ws", m.forgeDomain)) + p2pForgeWssComponent := multiaddr.StringCast(fmt.Sprintf("/tls/sni/*.%s/ws", m.forgeDomain)) return func(multiaddrs []multiaddr.Multiaddr) []multiaddr.Multiaddr { var skipForgeAddrs bool @@ -526,12 +527,14 @@ func (d *dns01P2PForgeSolver) Present(ctx context.Context, challenge acme.Challe } func (d *dns01P2PForgeSolver) CleanUp(ctx context.Context, challenge acme.Challenge) error { - //TODO: Should we implement this, or is doing delete and Last-Writer-Wins enough? + // TODO: Should we implement this, or is doing delete and Last-Writer-Wins enough? return nil } -var _ acmez.Solver = (*dns01P2PForgeSolver)(nil) -var _ acmez.Waiter = (*dns01P2PForgeSolver)(nil) +var ( + _ acmez.Solver = (*dns01P2PForgeSolver)(nil) + _ acmez.Waiter = (*dns01P2PForgeSolver)(nil) +) func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain string, allowPrivateForgeAddrs bool, p2pForgeWssComponent multiaddr.Multiaddr, multiaddrs []multiaddr.Multiaddr, log *zap.SugaredLogger) []multiaddr.Multiaddr { retAddrs := make([]multiaddr.Multiaddr, 0, len(multiaddrs)) @@ -601,9 +604,9 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str continue } - pidStr := peer.ToCid(peerIDFn()).Encode(multibase.MustNewEncoder(multibase.Base36)) + b36PidStr := peer.ToCid(peerIDFn()).Encode(multibase.MustNewEncoder(multibase.Base36)) - newMaStr := fmt.Sprintf("%s/tcp/%s/tls/sni/%s.%s.%s/ws", ipMaStr, tcpPortStr, escapedIPStr, pidStr, forgeDomain) + newMaStr := fmt.Sprintf("%s/tcp/%s/tls/sni/%s.%s.%s/ws", ipMaStr, tcpPortStr, escapedIPStr, b36PidStr, forgeDomain) newMA, err := multiaddr.NewMultiaddr(newMaStr) if err != nil { log.Errorf("error creating new multiaddr from %q: %s", newMaStr, err.Error()) @@ -611,6 +614,15 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str continue } retAddrs = append(retAddrs, newMA) + + // upon successful new ipX mutliaddr creation, create an additional dns multiaddr + dnsMaStr := fmt.Sprintf("/dns/%s.%s.%s/tcp/%s/tls/ws", escapedIPStr, b36PidStr, forgeDomain, tcpPortStr) + dnsMA, err := multiaddr.NewMultiaddr(dnsMaStr) + if err == nil { + retAddrs = append(retAddrs, dnsMA) + } else { + log.Errorf("error creating new multiaddr from %q: %s", dnsMaStr, err.Error()) + } } return retAddrs } From 323077772ae79bf772ec8f4062c1774081071301 Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Fri, 10 Jan 2025 08:08:34 +0100 Subject: [PATCH 2/6] replacing ipX address with dnsX --- client/acme.go | 17 ++++------------- e2e_test.go | 2 +- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/client/acme.go b/client/acme.go index d894448..3583c48 100644 --- a/client/acme.go +++ b/client/acme.go @@ -554,18 +554,18 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str index := 0 var escapedIPStr string - var ipMaStr string + var ipVersion string var tcpPortStr string multiaddr.ForEach(withoutForgeWSS, func(c multiaddr.Component) bool { switch index { case 0: switch c.Protocol().Code { case multiaddr.P_IP4: - ipMaStr = c.String() + ipVersion = "4" ipAddr := c.Value() escapedIPStr = strings.ReplaceAll(ipAddr, ".", "-") case multiaddr.P_IP6: - ipMaStr = c.String() + ipVersion = "6" ipAddr := c.Value() escapedIPStr = strings.ReplaceAll(ipAddr, ":", "-") if escapedIPStr[0] == '-' { @@ -606,7 +606,7 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str b36PidStr := peer.ToCid(peerIDFn()).Encode(multibase.MustNewEncoder(multibase.Base36)) - newMaStr := fmt.Sprintf("%s/tcp/%s/tls/sni/%s.%s.%s/ws", ipMaStr, tcpPortStr, escapedIPStr, b36PidStr, forgeDomain) + newMaStr := fmt.Sprintf("/dns%s/%s.%s.%s/tcp/%s/tls/ws", ipVersion, escapedIPStr, b36PidStr, forgeDomain, tcpPortStr) newMA, err := multiaddr.NewMultiaddr(newMaStr) if err != nil { log.Errorf("error creating new multiaddr from %q: %s", newMaStr, err.Error()) @@ -614,15 +614,6 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str continue } retAddrs = append(retAddrs, newMA) - - // upon successful new ipX mutliaddr creation, create an additional dns multiaddr - dnsMaStr := fmt.Sprintf("/dns/%s.%s.%s/tcp/%s/tls/ws", escapedIPStr, b36PidStr, forgeDomain, tcpPortStr) - dnsMA, err := multiaddr.NewMultiaddr(dnsMaStr) - if err == nil { - retAddrs = append(retAddrs, dnsMA) - } else { - log.Errorf("error creating new multiaddr from %q: %s", dnsMaStr, err.Error()) - } } return retAddrs } diff --git a/e2e_test.go b/e2e_test.go index 1f59268..407178a 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -606,7 +606,7 @@ func TestLibp2pACMEE2E(t *testing.T) { if strings.Contains(as, "p2p-circuit") { continue } - if strings.Contains(as, "libp2p.direct/ws") { + if strings.Contains(as, "libp2p.direct/tcp/") && strings.Contains(as, "/tls/ws") { dialAddr = addr break } From 27b2ae25536cb00e2cf23c80b02139d3519f73f1 Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Tue, 14 Jan 2025 14:21:01 +0100 Subject: [PATCH 3/6] opt-in /dnsX/ addresses flag --- client/acme.go | 30 +++++++++++++++++++++++++----- e2e_test.go | 2 +- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/client/acme.go b/client/acme.go index 3583c48..1249f77 100644 --- a/client/acme.go +++ b/client/acme.go @@ -39,6 +39,7 @@ type P2PForgeCertMgr struct { cfg *certmagic.Config log *zap.SugaredLogger allowPrivateForgeAddresses bool + resolvedDNSMultiaddrs bool hasCert bool // tracking if we've received a certificate certCheckMx sync.RWMutex @@ -85,6 +86,7 @@ type P2PForgeCertMgrConfig struct { onCertLoaded func() log *zap.SugaredLogger allowPrivateForgeAddresses bool + resolvedDNSMultiaddrs bool } type P2PForgeCertMgrOptions func(*P2PForgeCertMgrConfig) error @@ -191,6 +193,15 @@ func WithLogger(log *zap.SugaredLogger) P2PForgeCertMgrOptions { } } +// WithResolvedDNSMultiaddrs enables advertising /dnsX/../tcp//tls/ws addresses +// instead of the default /ipX//tcp//tls/sni/../ws. +func WithResolvedDNSMultiaddrs() P2PForgeCertMgrOptions { + return func(config *P2PForgeCertMgrConfig) error { + config.resolvedDNSMultiaddrs = true + return nil + } +} + // newCertmagicConfig is p2p-forge/client-specific version of // certmagic.NewDefault() that ensures we have our own cert cache. This is // necessary to ensure cert maintenance spawned by NewCache does not share @@ -301,6 +312,7 @@ func NewP2PForgeCertMgr(opts ...P2PForgeCertMgrOptions) (*P2PForgeCertMgr, error cfg: certCfg, log: mgrCfg.log, allowPrivateForgeAddresses: mgrCfg.allowPrivateForgeAddresses, + resolvedDNSMultiaddrs: mgrCfg.resolvedDNSMultiaddrs, } certCfg.OnEvent = func(ctx context.Context, event string, data map[string]any) error { @@ -430,7 +442,7 @@ func (m *P2PForgeCertMgr) AddressFactory() config.AddrsFactory { tlsCfg := m.cfg.TLSConfig() tlsCfg.NextProtos = []string{"h2", "http/1.1"} // remove the ACME ALPN and set the HTTP 1.1 and 2 ALPNs - return m.createAddrsFactory(m.allowPrivateForgeAddresses) + return m.createAddrsFactory(m.allowPrivateForgeAddresses, m.resolvedDNSMultiaddrs) } // localCertExists returns true if a certificate matching passed name is already present in certmagic.Storage @@ -449,7 +461,7 @@ func certName(id peer.ID, suffixDomain string) string { return fmt.Sprintf("*.%s.%s", pb36, suffixDomain) } -func (m *P2PForgeCertMgr) createAddrsFactory(allowPrivateForgeAddrs bool) config.AddrsFactory { +func (m *P2PForgeCertMgr) createAddrsFactory(allowPrivateForgeAddrs bool, resolvedDNSMultiaddrs bool) config.AddrsFactory { p2pForgeWssComponent := multiaddr.StringCast(fmt.Sprintf("/tls/sni/*.%s/ws", m.forgeDomain)) return func(multiaddrs []multiaddr.Multiaddr) []multiaddr.Multiaddr { @@ -463,7 +475,7 @@ func (m *P2PForgeCertMgr) createAddrsFactory(allowPrivateForgeAddrs bool) config } m.certCheckMx.RUnlock() - return addrFactoryFn(skipForgeAddrs, func() peer.ID { return m.hostFn().ID() }, m.forgeDomain, allowPrivateForgeAddrs, p2pForgeWssComponent, multiaddrs, m.log) + return addrFactoryFn(skipForgeAddrs, func() peer.ID { return m.hostFn().ID() }, m.forgeDomain, allowPrivateForgeAddrs, resolvedDNSMultiaddrs, p2pForgeWssComponent, multiaddrs, m.log) } } @@ -536,7 +548,7 @@ var ( _ acmez.Waiter = (*dns01P2PForgeSolver)(nil) ) -func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain string, allowPrivateForgeAddrs bool, p2pForgeWssComponent multiaddr.Multiaddr, multiaddrs []multiaddr.Multiaddr, log *zap.SugaredLogger) []multiaddr.Multiaddr { +func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain string, allowPrivateForgeAddrs bool, resolvedDNSMultiaddrs bool, p2pForgeWssComponent multiaddr.Multiaddr, multiaddrs []multiaddr.Multiaddr, log *zap.SugaredLogger) []multiaddr.Multiaddr { retAddrs := make([]multiaddr.Multiaddr, 0, len(multiaddrs)) for _, a := range multiaddrs { if isRelayAddr(a) { @@ -555,6 +567,7 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str index := 0 var escapedIPStr string var ipVersion string + var ipMaStr string var tcpPortStr string multiaddr.ForEach(withoutForgeWSS, func(c multiaddr.Component) bool { switch index { @@ -562,10 +575,12 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str switch c.Protocol().Code { case multiaddr.P_IP4: ipVersion = "4" + ipMaStr = c.String() ipAddr := c.Value() escapedIPStr = strings.ReplaceAll(ipAddr, ".", "-") case multiaddr.P_IP6: ipVersion = "6" + ipMaStr = c.String() ipAddr := c.Value() escapedIPStr = strings.ReplaceAll(ipAddr, ":", "-") if escapedIPStr[0] == '-' { @@ -606,7 +621,12 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str b36PidStr := peer.ToCid(peerIDFn()).Encode(multibase.MustNewEncoder(multibase.Base36)) - newMaStr := fmt.Sprintf("/dns%s/%s.%s.%s/tcp/%s/tls/ws", ipVersion, escapedIPStr, b36PidStr, forgeDomain, tcpPortStr) + var newMaStr string + if resolvedDNSMultiaddrs { + newMaStr = fmt.Sprintf("/dns%s/%s.%s.%s/tcp/%s/tls/ws", ipVersion, escapedIPStr, b36PidStr, forgeDomain, tcpPortStr) + } else { + newMaStr = fmt.Sprintf("%s/tcp/%s/tls/sni/%s.%s.%s/ws", ipMaStr, tcpPortStr, escapedIPStr, b36PidStr, forgeDomain) + } newMA, err := multiaddr.NewMultiaddr(newMaStr) if err != nil { log.Errorf("error creating new multiaddr from %q: %s", newMaStr, err.Error()) diff --git a/e2e_test.go b/e2e_test.go index 407178a..1f59268 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -606,7 +606,7 @@ func TestLibp2pACMEE2E(t *testing.T) { if strings.Contains(as, "p2p-circuit") { continue } - if strings.Contains(as, "libp2p.direct/tcp/") && strings.Contains(as, "/tls/ws") { + if strings.Contains(as, "libp2p.direct/ws") { dialAddr = addr break } From 20c73e58a31247b3d346618b9ae92949c54787e8 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 15 Jan 2025 01:04:46 +0100 Subject: [PATCH 4/6] docs: WithShortForgeAddrs(true|false) --- client/acme.go | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/client/acme.go b/client/acme.go index 1249f77..ac2a195 100644 --- a/client/acme.go +++ b/client/acme.go @@ -39,7 +39,7 @@ type P2PForgeCertMgr struct { cfg *certmagic.Config log *zap.SugaredLogger allowPrivateForgeAddresses bool - resolvedDNSMultiaddrs bool + produceShortAddrs bool hasCert bool // tracking if we've received a certificate certCheckMx sync.RWMutex @@ -86,7 +86,7 @@ type P2PForgeCertMgrConfig struct { onCertLoaded func() log *zap.SugaredLogger allowPrivateForgeAddresses bool - resolvedDNSMultiaddrs bool + produceShortAddrs bool } type P2PForgeCertMgrOptions func(*P2PForgeCertMgrConfig) error @@ -186,18 +186,26 @@ func WithAllowPrivateForgeAddrs() P2PForgeCertMgrOptions { } } -func WithLogger(log *zap.SugaredLogger) P2PForgeCertMgrOptions { +// WithShortForgeAddrs controls if final addresses produced by p2p-forge addr +// factory are short and start with /dnsX or are longer and the DNS name is +// fully resolved into /ipX /sni components. +// +// Using /dnsX may be beneficial when interop with older libp2p clients is +// required, or when shorter addresses are preferred. +// +// Example multiaddr formats: +// - When true: /dnsX/../tcp//tls/ws +// - When false: /ipX//tcp//tls/sni/../ws +func WithShortForgeAddrs(produceShortAddrs bool) P2PForgeCertMgrOptions { return func(config *P2PForgeCertMgrConfig) error { - config.log = log + config.produceShortAddrs = produceShortAddrs return nil } } -// WithResolvedDNSMultiaddrs enables advertising /dnsX/../tcp//tls/ws addresses -// instead of the default /ipX//tcp//tls/sni/../ws. -func WithResolvedDNSMultiaddrs() P2PForgeCertMgrOptions { +func WithLogger(log *zap.SugaredLogger) P2PForgeCertMgrOptions { return func(config *P2PForgeCertMgrConfig) error { - config.resolvedDNSMultiaddrs = true + config.log = log return nil } } @@ -312,7 +320,7 @@ func NewP2PForgeCertMgr(opts ...P2PForgeCertMgrOptions) (*P2PForgeCertMgr, error cfg: certCfg, log: mgrCfg.log, allowPrivateForgeAddresses: mgrCfg.allowPrivateForgeAddresses, - resolvedDNSMultiaddrs: mgrCfg.resolvedDNSMultiaddrs, + produceShortAddrs: mgrCfg.produceShortAddrs, } certCfg.OnEvent = func(ctx context.Context, event string, data map[string]any) error { @@ -442,7 +450,7 @@ func (m *P2PForgeCertMgr) AddressFactory() config.AddrsFactory { tlsCfg := m.cfg.TLSConfig() tlsCfg.NextProtos = []string{"h2", "http/1.1"} // remove the ACME ALPN and set the HTTP 1.1 and 2 ALPNs - return m.createAddrsFactory(m.allowPrivateForgeAddresses, m.resolvedDNSMultiaddrs) + return m.createAddrsFactory(m.allowPrivateForgeAddresses, m.produceShortAddrs) } // localCertExists returns true if a certificate matching passed name is already present in certmagic.Storage @@ -461,7 +469,7 @@ func certName(id peer.ID, suffixDomain string) string { return fmt.Sprintf("*.%s.%s", pb36, suffixDomain) } -func (m *P2PForgeCertMgr) createAddrsFactory(allowPrivateForgeAddrs bool, resolvedDNSMultiaddrs bool) config.AddrsFactory { +func (m *P2PForgeCertMgr) createAddrsFactory(allowPrivateForgeAddrs bool, produceShortAddrs bool) config.AddrsFactory { p2pForgeWssComponent := multiaddr.StringCast(fmt.Sprintf("/tls/sni/*.%s/ws", m.forgeDomain)) return func(multiaddrs []multiaddr.Multiaddr) []multiaddr.Multiaddr { @@ -475,7 +483,7 @@ func (m *P2PForgeCertMgr) createAddrsFactory(allowPrivateForgeAddrs bool, resolv } m.certCheckMx.RUnlock() - return addrFactoryFn(skipForgeAddrs, func() peer.ID { return m.hostFn().ID() }, m.forgeDomain, allowPrivateForgeAddrs, resolvedDNSMultiaddrs, p2pForgeWssComponent, multiaddrs, m.log) + return addrFactoryFn(skipForgeAddrs, func() peer.ID { return m.hostFn().ID() }, m.forgeDomain, allowPrivateForgeAddrs, produceShortAddrs, p2pForgeWssComponent, multiaddrs, m.log) } } @@ -548,7 +556,7 @@ var ( _ acmez.Waiter = (*dns01P2PForgeSolver)(nil) ) -func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain string, allowPrivateForgeAddrs bool, resolvedDNSMultiaddrs bool, p2pForgeWssComponent multiaddr.Multiaddr, multiaddrs []multiaddr.Multiaddr, log *zap.SugaredLogger) []multiaddr.Multiaddr { +func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain string, allowPrivateForgeAddrs bool, produceShortAddrs bool, p2pForgeWssComponent multiaddr.Multiaddr, multiaddrs []multiaddr.Multiaddr, log *zap.SugaredLogger) []multiaddr.Multiaddr { retAddrs := make([]multiaddr.Multiaddr, 0, len(multiaddrs)) for _, a := range multiaddrs { if isRelayAddr(a) { @@ -622,7 +630,7 @@ func addrFactoryFn(skipForgeAddrs bool, peerIDFn func() peer.ID, forgeDomain str b36PidStr := peer.ToCid(peerIDFn()).Encode(multibase.MustNewEncoder(multibase.Base36)) var newMaStr string - if resolvedDNSMultiaddrs { + if produceShortAddrs { newMaStr = fmt.Sprintf("/dns%s/%s.%s.%s/tcp/%s/tls/ws", ipVersion, escapedIPStr, b36PidStr, forgeDomain, tcpPortStr) } else { newMaStr = fmt.Sprintf("%s/tcp/%s/tls/sni/%s.%s.%s/ws", ipMaStr, tcpPortStr, escapedIPStr, b36PidStr, forgeDomain) From 5fbe0889184b29e0060ecd68bef941276f2cbfbc Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 15 Jan 2025 01:09:51 +0100 Subject: [PATCH 5/6] test: TestLibp2pACMEE2E and WithShortForgeAddrs tests guarding implicit default, and explicit user preference --- e2e_test.go | 261 +++++++++++++++++++++++++++++----------------------- 1 file changed, 144 insertions(+), 117 deletions(-) diff --git a/e2e_test.go b/e2e_test.go index 1f59268..acfd33c 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -484,139 +484,166 @@ 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) + tests := []struct { + name string + clientOpts []client.P2PForgeCertMgrOptions + }{ + { + name: "default opts", + clientOpts: []client.P2PForgeCertMgrOptions{}, + }, + { + name: "explicit WithShortForgeAddrs(true)", + clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(true)}, + }, + { + name: "explicit WithShortForgeAddrs(false)", + clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(false)}, + }, + } - wfeImpl := pebbleWFE.New(logger, db, va, ca, false, false, 3, 5) - muxHandler := wfeImpl.Handler() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { - acmeHTTPListener, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Fatal(err) - } - defer acmeHTTPListener.Close() + 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) - // 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) - } + wfeImpl := pebbleWFE.New(logger, db, va, ca, false, false, 3, 5) + muxHandler := wfeImpl.Handler() - // 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) - } + acmeHTTPListener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer acmeHTTPListener.Close() - // Create a TLS configuration with the certificate - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } + // 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) + } - // Wrap the listener with TLS - acmeHTTPListener = tls.NewListener(acmeHTTPListener, tlsConfig) + // 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) + } - go func() { - http.Serve(acmeHTTPListener, muxHandler) - }() + // Create a TLS configuration with the certificate + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + // Wrap the listener with TLS + acmeHTTPListener = tls.NewListener(acmeHTTPListener, tlsConfig) - cas := x509.NewCertPool() - cas.AppendCertsFromPEM(certPEM) - - acmeEndpoint := fmt.Sprintf("https://%s%s", acmeHTTPListener.Addr(), pebbleWFE.DirectoryPath) - certLoaded := make(chan bool, 1) - - certMgr, err := client.NewP2PForgeCertMgr( - client.WithForgeDomain(forge), client.WithForgeRegistrationEndpoint(fmt.Sprintf("http://127.0.0.1:%d", httpPort)), client.WithCAEndpoint(acmeEndpoint), client.WithTrustedRoots(cas), - client.WithModifiedForgeRequest(func(req *http.Request) error { - req.Host = forgeRegistration - req.Header.Set(authForgeHeader, authToken) - return nil - }), - client.WithAllowPrivateForgeAddrs(), - client.WithOnCertLoaded(func() { - certLoaded <- true - })) - if err != nil { - t.Fatal(err) - } - certMgr.Start() - defer certMgr.Stop() - - h, err := libp2p.New(libp2p.ChainOptions( - libp2p.DefaultListenAddrs, - libp2p.Transport(tcp.NewTCPTransport), - libp2p.Transport(libp2pquic.NewTransport), - libp2p.Transport(libp2pwebtransport.New), - libp2p.Transport(libp2pwebrtc.New), - - libp2p.ListenAddrStrings( - certMgr.AddrStrings()..., // TODO reuse tcp port for ws - ), - libp2p.Transport(libp2pws.New, libp2pws.WithTLSConfig(certMgr.TLSConfig())), - libp2p.AddrsFactory(certMgr.AddressFactory()), - )) - if err != nil { - t.Fatal(err) - } - certMgr.ProvideHost(h) + go func() { + http.Serve(acmeHTTPListener, muxHandler) + }() - cp := x509.NewCertPool() - cp.AddCert(ca.GetRootCert(0).Cert) - tlsCfgWithTestCA := &tls.Config{RootCAs: cp} + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() - 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 + cas := x509.NewCertPool() + cas.AppendCertsFromPEM(certPEM) + + acmeEndpoint := fmt.Sprintf("https://%s%s", acmeHTTPListener.Addr(), pebbleWFE.DirectoryPath) + certLoaded := make(chan bool, 1) + + clientOpts := append([]client.P2PForgeCertMgrOptions{ + client.WithForgeDomain(forge), client.WithForgeRegistrationEndpoint(fmt.Sprintf("http://127.0.0.1:%d", httpPort)), client.WithCAEndpoint(acmeEndpoint), client.WithTrustedRoots(cas), + client.WithModifiedForgeRequest(func(req *http.Request) error { + req.Host = forgeRegistration + req.Header.Set(authForgeHeader, authToken) + return nil + }), + client.WithAllowPrivateForgeAddrs(), + client.WithOnCertLoaded(func() { + certLoaded <- true + }), + }, tt.clientOpts...) + + certMgr, err := client.NewP2PForgeCertMgr(clientOpts...) + if err != nil { + t.Fatal(err) + } + certMgr.Start() + defer certMgr.Stop() + + h, err := libp2p.New(libp2p.ChainOptions( + libp2p.DefaultListenAddrs, + libp2p.Transport(tcp.NewTCPTransport), + libp2p.Transport(libp2pquic.NewTransport), + libp2p.Transport(libp2pwebtransport.New), + libp2p.Transport(libp2pwebrtc.New), + + libp2p.ListenAddrStrings( + certMgr.AddrStrings()..., // TODO reuse tcp port for ws + ), + libp2p.Transport(libp2pws.New, libp2pws.WithTLSConfig(certMgr.TLSConfig())), + libp2p.AddrsFactory(certMgr.AddressFactory()), + )) + if err != nil { + t.Fatal(err) + } + certMgr.ProvideHost(h) + + 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) } - 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(swarm.ResolverFromMaDNS{Resolver: customResolver})) - if err != nil { - t.Fatal(err) - } + h2, err := libp2p.New(libp2p.Transport(libp2pws.New, libp2pws.WithTLSClientConfig(tlsCfgWithTestCA)), + libp2p.MultiaddrResolver(swarm.ResolverFromMaDNS{Resolver: customResolver})) + if err != nil { + t.Fatal(err) + } - select { - case <-certLoaded: - case <-time.After(time.Second * 30): - t.Fatal("timed out waiting for certificate") - } + select { + case <-certLoaded: + case <-time.After(time.Second * 30): + t.Fatal("timed out waiting for certificate") + } - 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) - } + 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) + } - if err := h2.Connect(ctx, peer.AddrInfo{ID: h.ID(), Addrs: []multiaddr.Multiaddr{dialAddr}}); err != nil { - t.Fatal(err) + if err := h2.Connect(ctx, peer.AddrInfo{ID: h.ID(), Addrs: []multiaddr.Multiaddr{dialAddr}}); err != nil { + t.Fatal(err) + } + + }) } } From 52aa448a2e9073943aa77dfe61bb113deb32554b Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Wed, 15 Jan 2025 10:20:57 +0100 Subject: [PATCH 6/6] fix: tests --- e2e_test.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/e2e_test.go b/e2e_test.go index acfd33c..4a57443 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -484,21 +484,32 @@ func TestIPv6Lookup(t *testing.T) { } func TestLibp2pACMEE2E(t *testing.T) { + isValidResolvedForgeAddr := func(addr string) bool { + return strings.Contains(addr, "libp2p.direct/ws") + } + isValidShortForgeAddr := func(addr string) bool { + return strings.Contains(addr, "libp2p.direct/tcp/") && strings.Contains(addr, "/tls/ws") + } + tests := []struct { - name string - clientOpts []client.P2PForgeCertMgrOptions + name string + clientOpts []client.P2PForgeCertMgrOptions + isValidForgeAddr func(addr string) bool }{ { - name: "default opts", - clientOpts: []client.P2PForgeCertMgrOptions{}, + name: "default opts", + clientOpts: []client.P2PForgeCertMgrOptions{}, + isValidForgeAddr: isValidResolvedForgeAddr, }, { - name: "explicit WithShortForgeAddrs(true)", - clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(true)}, + name: "explicit WithShortForgeAddrs(true)", + clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(true)}, + isValidForgeAddr: isValidShortForgeAddr, }, { - name: "explicit WithShortForgeAddrs(false)", - clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(false)}, + name: "explicit WithShortForgeAddrs(false)", + clientOpts: []client.P2PForgeCertMgrOptions{client.WithShortForgeAddrs(false)}, + isValidForgeAddr: isValidResolvedForgeAddr, }, } @@ -630,7 +641,7 @@ func TestLibp2pACMEE2E(t *testing.T) { if strings.Contains(as, "p2p-circuit") { continue } - if strings.Contains(as, "libp2p.direct/ws") { + if tt.isValidForgeAddr(as) { dialAddr = addr break }