Skip to content

Commit

Permalink
Add relay e2e tests and output some mermaid sequence diagrams (#691)
Browse files Browse the repository at this point in the history
  • Loading branch information
nbrownus authored Jun 27, 2022
1 parent 7b92877 commit 0d1ee42
Show file tree
Hide file tree
Showing 10 changed files with 416 additions and 32 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ jobs:
- name: End 2 end
run: make e2evv

- uses: actions/upload-artifact@v3
with:
name: e2e packet flow
path: e2e/mermaid/
if-no-files-found: warn

test:
name: Build and test on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -78,3 +84,9 @@ jobs:

- name: End 2 end
run: make e2evv

- uses: actions/upload-artifact@v3
with:
name: e2e packet flow
path: e2e/mermaid/
if-no-files-found: warn
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
/cpu.pprof
/build
/*.tar.gz
/e2e/mermaid/
22 changes: 22 additions & 0 deletions control_tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ func (c *Control) InjectLightHouseAddr(vpnIp net.IP, toAddr *net.UDPAddr) {
}
}

// InjectRelays will push relayVpnIps into the local lighthouse cache for the vpnIp
// This is necessary to inform an initiator of possible relays for communicating with a responder
func (c *Control) InjectRelays(vpnIp net.IP, relayVpnIps []net.IP) {
c.f.lightHouse.Lock()
remoteList := c.f.lightHouse.unlockedGetRemoteList(iputil.Ip2VpnIp(vpnIp))
remoteList.Lock()
defer remoteList.Unlock()
c.f.lightHouse.Unlock()

iVpnIp := iputil.Ip2VpnIp(vpnIp)
uVpnIp := []uint32{}
for _, rVPnIp := range relayVpnIps {
uVpnIp = append(uVpnIp, uint32(iputil.Ip2VpnIp(rVPnIp)))
}

remoteList.unlockedSetRelay(iVpnIp, iVpnIp, uVpnIp)
}

// GetFromTun will pull a packet off the tun side of nebula
func (c *Control) GetFromTun(block bool) []byte {
return c.f.inside.(*overlay.TestTun).Get(block)
Expand Down Expand Up @@ -118,6 +136,10 @@ func (c *Control) InjectTunUDPPacket(toIp net.IP, toPort uint16, fromPort uint16
c.f.inside.(*overlay.TestTun).Send(buffer.Bytes())
}

func (c *Control) GetVpnIp() iputil.VpnIp {
return c.f.myVpnIp
}

func (c *Control) GetUDPAddr() string {
return c.f.outside.Addr.String()
}
Expand Down
66 changes: 49 additions & 17 deletions e2e/handshakes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import (

func TestGoodHandshake(t *testing.T) {
ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{})
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me", net.IP{10, 0, 0, 1})
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 2})
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me", net.IP{10, 0, 0, 1}, nil)
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 2}, nil)

// Put their info in our lighthouse
myControl.InjectLightHouseAddr(theirVpnIp, theirUdpAddr)
Expand Down Expand Up @@ -57,7 +57,9 @@ func TestGoodHandshake(t *testing.T) {
assertUdpPacket(t, []byte("Hi from me"), myCachedPacket, myVpnIp, theirVpnIp, 80, 80)

t.Log("Do a bidirectional tunnel test")
assertTunnel(t, myVpnIp, theirVpnIp, myControl, theirControl, router.NewR(myControl, theirControl))
r := router.NewR(t, myControl, theirControl)
defer r.RenderFlow()
assertTunnel(t, myVpnIp, theirVpnIp, myControl, theirControl, r)

myControl.Stop()
theirControl.Stop()
Expand All @@ -70,9 +72,9 @@ func TestWrongResponderHandshake(t *testing.T) {
// The IPs here are chosen on purpose:
// The current remote handling will sort by preference, public, and then lexically.
// So we need them to have a higher address than evil (we could apply a preference though)
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me", net.IP{10, 0, 0, 100})
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 99})
evilControl, evilVpnIp, evilUdpAddr := newSimpleServer(ca, caKey, "evil", net.IP{10, 0, 0, 2})
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me", net.IP{10, 0, 0, 100}, nil)
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 99}, nil)
evilControl, evilVpnIp, evilUdpAddr := newSimpleServer(ca, caKey, "evil", net.IP{10, 0, 0, 2}, nil)

// Add their real udp addr, which should be tried after evil.
myControl.InjectLightHouseAddr(theirVpnIp, theirUdpAddr)
Expand All @@ -81,7 +83,8 @@ func TestWrongResponderHandshake(t *testing.T) {
myControl.InjectLightHouseAddr(theirVpnIp, evilUdpAddr)

// Build a router so we don't have to reason who gets which packet
r := router.NewR(myControl, theirControl, evilControl)
r := router.NewR(t, myControl, theirControl, evilControl)
defer r.RenderFlow()

// Start the servers
myControl.Start()
Expand Down Expand Up @@ -130,15 +133,16 @@ func TestWrongResponderHandshake(t *testing.T) {

func Test_Case1_Stage1Race(t *testing.T) {
ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{})
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me ", net.IP{10, 0, 0, 1})
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 2})
myControl, myVpnIp, myUdpAddr := newSimpleServer(ca, caKey, "me ", net.IP{10, 0, 0, 1}, nil)
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them", net.IP{10, 0, 0, 2}, nil)

// Put their info in our lighthouse and vice versa
myControl.InjectLightHouseAddr(theirVpnIp, theirUdpAddr)
theirControl.InjectLightHouseAddr(myVpnIp, myUdpAddr)

// Build a router so we don't have to reason who gets which packet
r := router.NewR(myControl, theirControl)
r := router.NewR(t, myControl, theirControl)
defer r.RenderFlow()

// Start the servers
myControl.Start()
Expand All @@ -152,16 +156,16 @@ func Test_Case1_Stage1Race(t *testing.T) {
myHsForThem := myControl.GetFromUDP(true)
theirHsForMe := theirControl.GetFromUDP(true)

t.Log("Now inject both stage 1 handshake packets")
myControl.InjectUDPPacket(theirHsForMe)
theirControl.InjectUDPPacket(myHsForThem)
r.Log("Now inject both stage 1 handshake packets")
r.InjectUDPPacket(theirControl, myControl, theirHsForMe)
r.InjectUDPPacket(myControl, theirControl, myHsForThem)
//TODO: they should win, grab their index for me and make sure I use it in the end.

t.Log("They should not have a stage 2 (won the race) but I should send one")
theirControl.InjectUDPPacket(myControl.GetFromUDP(true))
r.Log("They should not have a stage 2 (won the race) but I should send one")
r.InjectUDPPacket(myControl, theirControl, myControl.GetFromUDP(true))

t.Log("Route for me until I send a message packet to them")
myControl.WaitForType(1, 0, theirControl)
r.Log("Route for me until I send a message packet to them")
r.RouteForAllUntilAfterMsgTypeTo(theirControl, header.Message, header.MessageNone)

t.Log("My cached packet should be received by them")
myCachedPacket := theirControl.GetFromTun(true)
Expand All @@ -182,4 +186,32 @@ func Test_Case1_Stage1Race(t *testing.T) {
//TODO: assert hostmaps
}

func TestRelays(t *testing.T) {
ca, _, caKey, _ := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []*net.IPNet{}, []*net.IPNet{}, []string{})
myControl, myVpnIp, _ := newSimpleServer(ca, caKey, "me ", net.IP{10, 0, 0, 1}, m{"relay": m{"use_relays": true}})
relayControl, relayVpnIp, relayUdpAddr := newSimpleServer(ca, caKey, "relay ", net.IP{10, 0, 0, 128}, m{"relay": m{"am_relay": true}})
theirControl, theirVpnIp, theirUdpAddr := newSimpleServer(ca, caKey, "them ", net.IP{10, 0, 0, 2}, m{"relay": m{"use_relays": true}})

// Teach my how to get to the relay and that their can be reached via the relay
myControl.InjectLightHouseAddr(relayVpnIp, relayUdpAddr)
myControl.InjectRelays(theirVpnIp, []net.IP{relayVpnIp})
relayControl.InjectLightHouseAddr(theirVpnIp, theirUdpAddr)

// Build a router so we don't have to reason who gets which packet
r := router.NewR(t, myControl, relayControl, theirControl)
defer r.RenderFlow()

// Start the servers
myControl.Start()
relayControl.Start()
theirControl.Start()

t.Log("Trigger a handshake from me to them via the relay")
myControl.InjectTunUDPPacket(theirVpnIp, 80, 80, []byte("Hi from me"))

p := r.RouteForAllUntilTxTun(theirControl)
assertUdpPacket(t, []byte("Hi from me"), p, myVpnIp, theirVpnIp, 80, 80)
//TODO: assert we actually used the relay even though it should be impossible for a tunnel to have occurred without it
}

//TODO: add a test with many lies
12 changes: 11 additions & 1 deletion e2e/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/imdario/mergo"
"github.com/sirupsen/logrus"
"github.com/slackhq/nebula"
"github.com/slackhq/nebula/cert"
Expand All @@ -30,7 +31,7 @@ import (
type m map[string]interface{}

// newSimpleServer creates a nebula instance with many assumptions
func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, udpIp net.IP) (*nebula.Control, net.IP, *net.UDPAddr) {
func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, udpIp net.IP, overrides m) (*nebula.Control, net.IP, *net.UDPAddr) {
l := NewTestLogger()

vpnIpNet := &net.IPNet{IP: make([]byte, len(udpIp)), Mask: net.IPMask{255, 255, 255, 0}}
Expand Down Expand Up @@ -78,6 +79,15 @@ func newSimpleServer(caCrt *cert.NebulaCertificate, caKey []byte, name string, u
"level": l.Level.String(),
},
}

if overrides != nil {
err = mergo.Merge(&overrides, mc, mergo.WithAppendSlice)
if err != nil {
panic(err)
}
mc = overrides
}

cb, err := yaml.Marshal(mc)
if err != nil {
panic(err)
Expand Down
Loading

0 comments on commit 0d1ee42

Please sign in to comment.