rebind_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. //go:build e2e_testing
  2. // +build e2e_testing
  3. package e2e
  4. import (
  5. "net/netip"
  6. "testing"
  7. "time"
  8. "github.com/slackhq/nebula"
  9. "github.com/slackhq/nebula/cert"
  10. "github.com/slackhq/nebula/cert_test"
  11. "github.com/slackhq/nebula/e2e/router"
  12. "github.com/slackhq/nebula/header"
  13. "github.com/slackhq/nebula/udp"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/require"
  16. )
  17. // reportedAddrs is what the lighthouse would hand a peer asking where vpnAddr is.
  18. func reportedAddrs(t *testing.T, lh *nebula.Control, vpnAddr netip.Addr) []netip.AddrPort {
  19. t.Helper()
  20. cm := lh.QueryLighthouse(vpnAddr)
  21. if cm == nil {
  22. return nil
  23. }
  24. var out []netip.AddrPort
  25. for _, c := range *cm {
  26. out = append(out, c.Reported...)
  27. out = append(out, c.Learned...)
  28. }
  29. return out
  30. }
  31. // waitForLighthouseMsg routes until a lighthouse message lands on lh, or gives up. Reports whether one arrived.
  32. func waitForLighthouseMsg(t *testing.T, r *router.R, lh *nebula.Control, wait time.Duration) bool {
  33. t.Helper()
  34. h := &header.H{}
  35. return r.RouteForAllExitFuncOrTimeout(wait, func(p *udp.Packet, c *nebula.Control) router.ExitType {
  36. if c != lh {
  37. return router.KeepRouting
  38. }
  39. // Punches are a single byte and never parse, they are just not what we are after
  40. if err := h.Parse(p.Data); err != nil {
  41. return router.KeepRouting
  42. }
  43. if h.Type == header.LightHouse {
  44. return router.RouteAndExit
  45. }
  46. return router.KeepRouting
  47. })
  48. }
  49. // A laptop that changes networks has to tell the lighthouse promptly, otherwise the lighthouse keeps handing peers
  50. // the old address and their punches land nowhere. On a long lighthouse interval the only thing that closes that
  51. // window is the rebind, which on darwin the network change monitor drives. The e2e build compiles the monitor out,
  52. // so we call RebindUDPServer directly, which is the same thing the monitor does.
  53. func TestRebindSendsLighthouseUpdate(t *testing.T) {
  54. t.Parallel()
  55. ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
  56. lhControl, lhVpnIpNet, lhUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "lh", "10.128.0.1/24", m{
  57. "lighthouse": m{"am_lighthouse": true},
  58. })
  59. // 600s interval, so nothing scheduled can send an update during this test. A rebind is the only thing that can.
  60. myControl, _, _, _ := newSimpleServer(cert.Version2, ca, caKey, "me", "10.128.0.2/24", m{
  61. "lighthouse": m{
  62. "hosts": []any{lhVpnIpNet[0].Addr().String()},
  63. "interval": 600,
  64. },
  65. "static_host_map": m{
  66. lhVpnIpNet[0].Addr().String(): []any{lhUdpAddr.String()},
  67. },
  68. })
  69. r := router.NewR(t, lhControl, myControl)
  70. defer r.RenderFlow()
  71. lhControl.Start()
  72. myControl.Start()
  73. // Let the startup registration finish, then clear everything it left behind
  74. require.True(t, waitForLighthouseMsg(t, r, lhControl, time.Second*5), "expected an initial registration")
  75. r.RouteFor(time.Millisecond * 400)
  76. // Nothing should be talking to the lighthouse on its own now
  77. require.False(t, waitForLighthouseMsg(t, r, lhControl, time.Millisecond*200),
  78. "nothing should reach the lighthouse before the rebind")
  79. myControl.RebindUDPServer()
  80. assert.True(t, waitForLighthouseMsg(t, r, lhControl, time.Second*5),
  81. "a rebind should push an update to the lighthouse rather than waiting out the interval")
  82. lhControl.Stop()
  83. myControl.Stop()
  84. }
  85. // The other half of a rebind: every live tunnel requeries the lighthouse on its next send. That query is what makes
  86. // the lighthouse tell the peer to punch toward our new address, which is the part that actually revives a tunnel
  87. // whose remote NAT state died while we were on a different network.
  88. func TestRebindRequeriesPeersOnNextSend(t *testing.T) {
  89. t.Parallel()
  90. ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
  91. lhControl, lhVpnIpNet, lhUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "lh", "10.128.0.1/24", m{
  92. "lighthouse": m{"am_lighthouse": true},
  93. })
  94. lhCfg := m{
  95. "lighthouse": m{
  96. "hosts": []any{lhVpnIpNet[0].Addr().String()},
  97. "interval": 600,
  98. // Without this the peers advertise this machine's real addresses and then try to punch at them,
  99. // which the router has no route for.
  100. "local_allow_list": m{
  101. "10.0.0.0/24": true,
  102. "::/0": false,
  103. },
  104. },
  105. "static_host_map": m{
  106. lhVpnIpNet[0].Addr().String(): []any{lhUdpAddr.String()},
  107. },
  108. }
  109. myControl, myVpnIpNet, myUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "me", "10.128.0.2/24", lhCfg)
  110. theirControl, theirVpnIpNet, theirUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "them", "10.128.0.3/24", lhCfg)
  111. r := router.NewR(t, lhControl, myControl, theirControl)
  112. defer r.RenderFlow()
  113. lhControl.Start()
  114. myControl.Start()
  115. theirControl.Start()
  116. r.RouteFor(time.Millisecond * 500)
  117. // Point the peers at each other directly, this test is about the rebind and not about lighthouse discovery
  118. myControl.InjectLightHouseAddr(theirVpnIpNet[0].Addr(), theirUdpAddr)
  119. theirControl.InjectLightHouseAddr(myVpnIpNet[0].Addr(), myUdpAddr)
  120. myControl.InjectTunPacket(BuildTunUDPPacket(theirVpnIpNet[0].Addr(), 80, myVpnIpNet[0].Addr(), 80, []byte("initial")))
  121. r.RouteFor(time.Second)
  122. require.NotNil(t, myControl.GetHostInfoByVpnAddr(theirVpnIpNet[0].Addr(), false), "expected a tunnel to them")
  123. r.RouteFor(time.Millisecond * 300)
  124. // Assert on what the peer sees rather than on lighthouse traffic. A query for them makes the lighthouse send
  125. // them a punch notification, which is the whole point. Our own update to the lighthouse sends them nothing,
  126. // so this cannot be satisfied by the update the rebind itself pushes.
  127. myControl.InjectTunPacket(BuildTunUDPPacket(theirVpnIpNet[0].Addr(), 80, myVpnIpNet[0].Addr(), 80, []byte("quiet")))
  128. require.False(t, waitForLighthouseMsg(t, r, theirControl, time.Millisecond*300),
  129. "an ordinary send should not requery the lighthouse")
  130. myControl.RebindUDPServer()
  131. r.RouteFor(time.Millisecond * 300) // let the update the rebind itself sends pass by
  132. myControl.InjectTunPacket(BuildTunUDPPacket(theirVpnIpNet[0].Addr(), 80, myVpnIpNet[0].Addr(), 80, []byte("after rebind")))
  133. assert.True(t, waitForLighthouseMsg(t, r, theirControl, time.Second*5),
  134. "the first send after a rebind should requery the lighthouse, which then tells the peer to punch at us")
  135. lhControl.Stop()
  136. myControl.Stop()
  137. theirControl.Stop()
  138. }
  139. // The scenario this whole thing exists for: a laptop sleeps at the office and wakes up at home on a new address.
  140. // Until it tells the lighthouse, the lighthouse keeps handing peers the office address, so their punches land
  141. // nowhere and the tunnel stays dead. On a long interval the rebind is the only thing that closes that window.
  142. func TestRebindAdvertisesNewAddressAfterMove(t *testing.T) {
  143. t.Parallel()
  144. ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
  145. lhControl, lhVpnIpNet, lhUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "lh", "10.128.0.1/24", m{
  146. "lighthouse": m{"am_lighthouse": true},
  147. })
  148. myControl, myVpnIpNet, myUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "me", "10.128.0.2/24", m{
  149. "lighthouse": m{
  150. "hosts": []any{lhVpnIpNet[0].Addr().String()},
  151. "interval": 600,
  152. },
  153. "static_host_map": m{
  154. lhVpnIpNet[0].Addr().String(): []any{lhUdpAddr.String()},
  155. },
  156. })
  157. // Advertise wherever we currently are rather than this machine's real NICs, read fresh each time so a move
  158. // is picked up.
  159. myControl.SetLocalAddrsFn(func(*nebula.LocalAllowList) []netip.Addr {
  160. return []netip.Addr{myControl.GetUDPAddr().Addr()}
  161. })
  162. r := router.NewR(t, lhControl, myControl)
  163. defer r.RenderFlow()
  164. lhControl.Start()
  165. myControl.Start()
  166. require.True(t, waitForLighthouseMsg(t, r, lhControl, time.Second*5), "expected an initial registration")
  167. r.RouteFor(time.Millisecond * 400)
  168. require.Contains(t, reportedAddrs(t, lhControl, myVpnIpNet[0].Addr()), myUdpAddr,
  169. "the lighthouse should know the address we started on")
  170. // Wake up somewhere else
  171. newAddr := netip.MustParseAddrPort("10.0.0.99:4242")
  172. myControl.SetUDPAddr(newAddr)
  173. r.AddRoute(newAddr.Addr(), newAddr.Port(), myControl)
  174. // Nothing has told the lighthouse, and with interval 600 nothing scheduled will
  175. r.RouteFor(time.Millisecond * 400)
  176. require.NotContains(t, reportedAddrs(t, lhControl, myVpnIpNet[0].Addr()), newAddr,
  177. "the lighthouse should still be handing out the old address before the rebind")
  178. myControl.RebindUDPServer()
  179. require.True(t, waitForLighthouseMsg(t, r, lhControl, time.Second*5), "expected an update after the rebind")
  180. r.RouteFor(time.Millisecond * 400)
  181. assert.Contains(t, reportedAddrs(t, lhControl, myVpnIpNet[0].Addr()), newAddr,
  182. "after the rebind the lighthouse should hand peers our new address")
  183. lhControl.Stop()
  184. myControl.Stop()
  185. }
  186. // A relayed send records traffic but must not consume the rebind epoch. If it does, the next direct send to the
  187. // relay host sees the epoch already current and never requeries, so the far side is never told to punch at our
  188. // new address. This pins the SendVia call site, which the unit tests cannot reach.
  189. func TestRebindRequeriesAfterRelayedSend(t *testing.T) {
  190. t.Parallel()
  191. ca, _, caKey, _ := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{})
  192. // No lighthouse on purpose: it would hand out a direct address for them and nothing would relay.
  193. // Long connection manager timers so it never fires a direct test packet at the relay tunnel and bumps its
  194. // epoch mid-test, which is the only other thing that touches that tunnel and would flake the assertion below.
  195. myControl, myVpnIpNet, _, _ := newSimpleServer(cert.Version2, ca, caKey, "me", "10.128.0.1/24",
  196. m{"relay": m{"use_relays": true}, "timers": m{"connection_alive_interval": 3600, "pending_deletion_interval": 3600}})
  197. relayControl, relayVpnIpNet, relayUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "relay", "10.128.0.128/24", m{"relay": m{"am_relay": true}})
  198. theirControl, theirVpnIpNet, theirUdpAddr, _ := newSimpleServer(cert.Version2, ca, caKey, "them", "10.128.0.2/24", m{"relay": m{"use_relays": true}})
  199. myControl.InjectLightHouseAddr(relayVpnIpNet[0].Addr(), relayUdpAddr)
  200. myControl.InjectRelays(theirVpnIpNet[0].Addr(), []netip.Addr{relayVpnIpNet[0].Addr()})
  201. relayControl.InjectLightHouseAddr(theirVpnIpNet[0].Addr(), theirUdpAddr)
  202. r := router.NewR(t, myControl, relayControl, theirControl)
  203. defer r.RenderFlow()
  204. myControl.Start()
  205. relayControl.Start()
  206. theirControl.Start()
  207. myControl.InjectTunPacket(BuildTunUDPPacket(theirVpnIpNet[0].Addr(), 80, myVpnIpNet[0].Addr(), 80, []byte("establish")))
  208. r.RouteForAllUntilTxTun(theirControl)
  209. r.RouteFor(time.Millisecond * 500)
  210. hi := myControl.GetHostInfoByVpnAddr(theirVpnIpNet[0].Addr(), false)
  211. require.NotNil(t, hi, "expected a tunnel to them")
  212. require.NotEmpty(t, hi.CurrentRelaysToMe, "them must be reachable only via the relay for this test to mean anything")
  213. // sendNoMetrics only reaches SendVia when there is no direct remote, so pin that too. Without this the test
  214. // keeps passing while quietly sending direct and never exercising the relay path.
  215. require.False(t, hi.CurrentRemote.IsValid(), "them must have no direct remote, otherwise SendVia is never called")
  216. before, ok := myControl.GetRebindEpochFor(relayVpnIpNet[0].Addr())
  217. require.True(t, ok, "expected a tunnel to the relay")
  218. myControl.RebindUDPServer()
  219. // Traffic to them goes through SendVia on the relay tunnel. That must record traffic without consuming the
  220. // relay tunnel's own epoch edge, which belongs to the direct path.
  221. myControl.InjectTunPacket(BuildTunUDPPacket(theirVpnIpNet[0].Addr(), 80, myVpnIpNet[0].Addr(), 80, []byte("relayed")))
  222. r.RouteForAllUntilTxTun(theirControl)
  223. after, ok := myControl.GetRebindEpochFor(relayVpnIpNet[0].Addr())
  224. require.True(t, ok)
  225. assert.Equal(t, before, after,
  226. "a relayed send consumed the relay tunnel's rebind epoch, so the next direct send will not requery")
  227. myControl.Stop()
  228. relayControl.Stop()
  229. theirControl.Stop()
  230. }