From 0721dde24bd2e9600f50520db1eaa37492116faa Mon Sep 17 00:00:00 2001 From: JackDoan Date: Wed, 10 Sep 2025 10:57:04 -0500 Subject: [PATCH] working e2e test?! --- cert_test/cert.go | 27 ++++++++++++ e2e/helpers_test.go | 103 ++++++++++++++++++++++++++++++++++++++++++++ e2e/tunnels_test.go | 94 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+) diff --git a/cert_test/cert.go b/cert_test/cert.go index ebc6f52..7513431 100644 --- a/cert_test/cert.go +++ b/cert_test/cert.go @@ -114,6 +114,33 @@ func NewTestCert(v cert.Version, curve cert.Curve, ca cert.Certificate, key []by return c, pub, cert.MarshalPrivateKeyToPEM(curve, priv), pem } +func NewTestCertDifferentVersion(c cert.Certificate, v cert.Version, ca cert.Certificate, key []byte) (cert.Certificate, []byte) { + nc := &cert.TBSCertificate{ + Version: v, + Curve: c.Curve(), + Name: c.Name(), + Networks: c.Networks(), + UnsafeNetworks: c.UnsafeNetworks(), + Groups: c.Groups(), + NotBefore: time.Unix(c.NotBefore().Unix(), 0), + NotAfter: time.Unix(c.NotAfter().Unix(), 0), + PublicKey: c.PublicKey(), + IsCA: false, + } + + c, err := nc.Sign(ca, ca.Curve(), key) + if err != nil { + panic(err) + } + + pem, err := c.MarshalPEM() + if err != nil { + panic(err) + } + + return c, pem +} + func X25519Keypair() ([]byte, []byte) { privkey := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, privkey); err != nil { diff --git a/e2e/helpers_test.go b/e2e/helpers_test.go index a63b3d0..b1c0924 100644 --- a/e2e/helpers_test.go +++ b/e2e/helpers_test.go @@ -129,6 +129,109 @@ func newSimpleServer(v cert.Version, caCrt cert.Certificate, caKey []byte, name return control, vpnNetworks, udpAddr, c } +// newSimpleServer creates a nebula instance with fewer assumptions +func newServer(caCrt []cert.Certificate, certs []cert.Certificate, key []byte, overrides m) (*nebula.Control, []netip.Prefix, netip.AddrPort, *config.C) { + l := NewTestLogger() + + vpnNetworks := certs[len(certs)-1].Networks() + + var udpAddr netip.AddrPort + if vpnNetworks[0].Addr().Is4() { + budpIp := vpnNetworks[0].Addr().As4() + budpIp[1] -= 128 + udpAddr = netip.AddrPortFrom(netip.AddrFrom4(budpIp), 4242) + } else { + budpIp := vpnNetworks[0].Addr().As16() + // beef for funsies + budpIp[2] = 190 + budpIp[3] = 239 + udpAddr = netip.AddrPortFrom(netip.AddrFrom16(budpIp), 4242) + } + + caStr := "" + for _, ca := range caCrt { + x, err := ca.MarshalPEM() + if err != nil { + panic(err) + } + caStr += string(x) + } + certStr := "" + for _, c := range certs { + x, err := c.MarshalPEM() + if err != nil { + panic(err) + } + certStr += string(x) + } + + mc := m{ + "pki": m{ + "ca": caStr, + "cert": certStr, + "key": string(key), + }, + //"tun": m{"disabled": true}, + "firewall": m{ + "outbound": []m{{ + "proto": "any", + "port": "any", + "host": "any", + }}, + "inbound": []m{{ + "proto": "any", + "port": "any", + "host": "any", + }}, + }, + //"handshakes": m{ + // "try_interval": "1s", + //}, + "listen": m{ + "host": udpAddr.Addr().String(), + "port": udpAddr.Port(), + }, + "logging": m{ + "timestamp_format": fmt.Sprintf("%v 15:04:05.000000", certs[0].Name()), + "level": l.Level.String(), + }, + "timers": m{ + "pending_deletion_interval": 2, + "connection_alive_interval": 2, + }, + } + + if overrides != nil { + final := m{} + err := mergo.Merge(&final, overrides, mergo.WithAppendSlice) + if err != nil { + panic(err) + } + err = mergo.Merge(&final, mc, mergo.WithAppendSlice) + if err != nil { + panic(err) + } + mc = final + } + + cb, err := yaml.Marshal(mc) + if err != nil { + panic(err) + } + + c := config.NewC(l) + cStr := string(cb) + c.LoadString(cStr) + + control, err := nebula.Main(c, false, "e2e-test", l, nil) + + if err != nil { + panic(err) + } + + return control, vpnNetworks, udpAddr, c +} + type doneCb func() func deadline(t *testing.T, seconds time.Duration) doneCb { diff --git a/e2e/tunnels_test.go b/e2e/tunnels_test.go index 55974f0..dc278ba 100644 --- a/e2e/tunnels_test.go +++ b/e2e/tunnels_test.go @@ -4,12 +4,16 @@ package e2e import ( + "fmt" + "net/netip" "testing" "time" "github.com/slackhq/nebula/cert" "github.com/slackhq/nebula/cert_test" "github.com/slackhq/nebula/e2e/router" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) func TestDropInactiveTunnels(t *testing.T) { @@ -55,3 +59,93 @@ func TestDropInactiveTunnels(t *testing.T) { myControl.Stop() theirControl.Stop() } + +func TestCertUpgrade(t *testing.T) { + // The goal of this test is to ensure the shortest inactivity timeout will close the tunnel on both sides + // under ideal conditions + ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version1, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{}) + caB, err := ca.MarshalPEM() + if err != nil { + panic(err) + } + ca2, _, caKey2, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{}) + + ca2B, err := ca2.MarshalPEM() + if err != nil { + panic(err) + } + caStr := fmt.Sprintf("%s\n%s", caB, ca2B) + + myCert, _, myPrivKey, _ := cert_test.NewTestCert(cert.Version1, cert.Curve_CURVE25519, ca, caKey, "me", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{netip.MustParsePrefix("10.128.0.1/24")}, nil, []string{}) + _, myCert2Pem := cert_test.NewTestCertDifferentVersion(myCert, cert.Version2, ca2, caKey2) + + theirCert, _, theirPrivKey, _ := cert_test.NewTestCert(cert.Version1, cert.Curve_CURVE25519, ca, caKey, "them", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{netip.MustParsePrefix("10.128.0.2/24")}, nil, []string{}) + theirCert2, _ := cert_test.NewTestCertDifferentVersion(theirCert, cert.Version2, ca2, caKey2) + + myControl, myVpnIpNet, myUdpAddr, myC := newServer([]cert.Certificate{ca, ca2}, []cert.Certificate{myCert}, myPrivKey, m{}) + theirControl, theirVpnIpNet, theirUdpAddr, _ := newServer([]cert.Certificate{ca, ca2}, []cert.Certificate{theirCert, theirCert2}, theirPrivKey, m{}) + + // Share our underlay information + myControl.InjectLightHouseAddr(theirVpnIpNet[0].Addr(), theirUdpAddr) + theirControl.InjectLightHouseAddr(myVpnIpNet[0].Addr(), myUdpAddr) + + // Start the servers + myControl.Start() + theirControl.Start() + + r := router.NewR(t, myControl, theirControl) + defer r.RenderFlow() + + r.Log("Assert the tunnel between me and them works") + assertTunnel(t, myVpnIpNet[0].Addr(), theirVpnIpNet[0].Addr(), myControl, theirControl, r) + r.Log("yay") + //todo ??? + time.Sleep(1 * time.Second) + r.FlushAll() + + mc := m{ + "pki": m{ + "ca": caStr, + "cert": string(myCert2Pem), + "key": string(myPrivKey), + }, + //"tun": m{"disabled": true}, + "firewall": myC.Settings["firewall"], + //"handshakes": m{ + // "try_interval": "1s", + //}, + "listen": myC.Settings["listen"], + "logging": myC.Settings["logging"], + "timers": myC.Settings["timers"], + } + + cb, err := yaml.Marshal(mc) + if err != nil { + panic(err) + } + + r.Logf("reload new v2 config") + err = myC.ReloadConfigString(string(cb)) + assert.NoError(t, err) + r.Logf("yay") + r.Log("spin until their sees it") + for { + assertTunnel(t, myVpnIpNet[0].Addr(), theirVpnIpNet[0].Addr(), myControl, theirControl, r) + c := theirControl.GetHostInfoByVpnAddr(myVpnIpNet[0].Addr(), false) + if c == nil { + r.Log("nil") + } else { + version := c.Cert.Version() + r.Logf("version %d", version) + if version == cert.Version2 { + break + } + } + time.Sleep(time.Second) + } + + r.RenderHostmaps("Final hostmaps", myControl, theirControl) + + myControl.Stop() + theirControl.Stop() +}