lighthouse_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797
  1. package nebula
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "net/netip"
  8. "testing"
  9. "github.com/gaissmai/bart"
  10. "github.com/slackhq/nebula/cert"
  11. "github.com/slackhq/nebula/config"
  12. "github.com/slackhq/nebula/header"
  13. "github.com/slackhq/nebula/test"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/require"
  16. "go.yaml.in/yaml/v3"
  17. )
  18. func TestOldIPv4Only(t *testing.T) {
  19. // This test ensures our new ipv6 enabled LH protobuf IpAndPorts works with the old style to enable backwards compatibility
  20. b := []byte{8, 129, 130, 132, 80, 16, 10}
  21. var m V4AddrPort
  22. err := m.Unmarshal(b)
  23. require.NoError(t, err)
  24. ip := netip.MustParseAddr("10.1.1.1")
  25. bp := ip.As4()
  26. assert.Equal(t, binary.BigEndian.Uint32(bp[:]), m.GetAddr())
  27. }
  28. func testCertState(networks ...netip.Prefix) *CertState {
  29. cs := &CertState{
  30. myVpnNetworks: networks,
  31. myVpnNetworksTable: new(bart.Lite),
  32. myVpnAddrs: make([]netip.Addr, 0, len(networks)),
  33. myVpnAddrsTable: new(bart.Lite),
  34. }
  35. for _, n := range networks {
  36. cs.myVpnNetworksTable.Insert(n)
  37. cs.myVpnAddrs = append(cs.myVpnAddrs, n.Addr())
  38. cs.myVpnAddrsTable.Insert(netip.PrefixFrom(n.Addr(), n.Addr().BitLen()))
  39. }
  40. return cs
  41. }
  42. func Test_lhStaticMapping(t *testing.T) {
  43. l := test.NewLogger()
  44. myVpnNet := netip.MustParsePrefix("10.128.0.1/16")
  45. cs := testCertState(myVpnNet)
  46. lh1 := "10.128.0.2"
  47. c := config.NewC(l)
  48. c.Settings["lighthouse"] = map[string]any{"hosts": []any{lh1}}
  49. c.Settings["static_host_map"] = map[string]any{lh1: []any{"1.1.1.1:4242"}}
  50. _, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  51. require.NoError(t, err)
  52. lh2 := "10.128.0.3"
  53. c = config.NewC(l)
  54. c.Settings["lighthouse"] = map[string]any{"hosts": []any{lh1, lh2}}
  55. c.Settings["static_host_map"] = map[string]any{lh1: []any{"100.1.1.1:4242"}}
  56. _, err = NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  57. require.EqualError(t, err, "lighthouse 10.128.0.3 does not have a static_host_map entry")
  58. }
  59. func TestReloadLighthouseInterval(t *testing.T) {
  60. l := test.NewLogger()
  61. myVpnNet := netip.MustParsePrefix("10.128.0.1/16")
  62. cs := testCertState(myVpnNet)
  63. lh1 := "10.128.0.2"
  64. c := config.NewC(l)
  65. c.Settings["lighthouse"] = map[string]any{
  66. "hosts": []any{lh1},
  67. "interval": "1s",
  68. }
  69. c.Settings["static_host_map"] = map[string]any{lh1: []any{"1.1.1.1:4242"}}
  70. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  71. require.NoError(t, err)
  72. lh.ifce = &mockEncWriter{}
  73. // The first one routine is kicked off by main.go currently, lets make sure that one dies
  74. require.NoError(t, c.ReloadConfigString("lighthouse:\n interval: 5"))
  75. assert.Equal(t, int64(5), lh.interval.Load())
  76. // Subsequent calls are killed off by the LightHouse.Reload function
  77. require.NoError(t, c.ReloadConfigString("lighthouse:\n interval: 10"))
  78. assert.Equal(t, int64(10), lh.interval.Load())
  79. // If this completes then nothing is stealing our reload routine
  80. require.NoError(t, c.ReloadConfigString("lighthouse:\n interval: 11"))
  81. assert.Equal(t, int64(11), lh.interval.Load())
  82. }
  83. func BenchmarkLighthouseHandleRequest(b *testing.B) {
  84. l := test.NewLogger()
  85. myVpnNet := netip.MustParsePrefix("10.128.0.1/0")
  86. cs := testCertState(myVpnNet)
  87. c := config.NewC(l)
  88. lh, err := NewLightHouseFromConfig(b.Context(), l, c, cs, nil, nil)
  89. require.NoError(b, err)
  90. hAddr := netip.MustParseAddrPort("4.5.6.7:12345")
  91. hAddr2 := netip.MustParseAddrPort("4.5.6.7:12346")
  92. vpnIp3 := netip.MustParseAddr("0.0.0.3")
  93. lh.addrMap[vpnIp3] = NewRemoteList([]netip.Addr{vpnIp3}, nil)
  94. lh.addrMap[vpnIp3].unlockedSetV4(
  95. vpnIp3,
  96. vpnIp3,
  97. []*V4AddrPort{
  98. netAddrToProtoV4AddrPort(hAddr.Addr(), hAddr.Port()),
  99. netAddrToProtoV4AddrPort(hAddr2.Addr(), hAddr2.Port()),
  100. },
  101. func(netip.Addr, *V4AddrPort) bool { return true },
  102. )
  103. rAddr := netip.MustParseAddrPort("1.2.2.3:12345")
  104. rAddr2 := netip.MustParseAddrPort("1.2.2.3:12346")
  105. vpnIp2 := netip.MustParseAddr("0.0.0.3")
  106. lh.addrMap[vpnIp2] = NewRemoteList([]netip.Addr{vpnIp2}, nil)
  107. lh.addrMap[vpnIp2].unlockedSetV4(
  108. vpnIp3,
  109. vpnIp3,
  110. []*V4AddrPort{
  111. netAddrToProtoV4AddrPort(rAddr.Addr(), rAddr.Port()),
  112. netAddrToProtoV4AddrPort(rAddr2.Addr(), rAddr2.Port()),
  113. },
  114. func(netip.Addr, *V4AddrPort) bool { return true },
  115. )
  116. mw := &mockEncWriter{}
  117. hi := []netip.Addr{vpnIp2}
  118. b.Run("notfound", func(b *testing.B) {
  119. lhh := lh.NewRequestHandler()
  120. req := &NebulaMeta{
  121. Type: NebulaMeta_HostQuery,
  122. Details: &NebulaMetaDetails{
  123. OldVpnAddr: 4,
  124. V4AddrPorts: nil,
  125. },
  126. }
  127. p, err := req.Marshal()
  128. require.NoError(b, err)
  129. for n := 0; n < b.N; n++ {
  130. lhh.HandleRequest(rAddr, hi, p, mw)
  131. }
  132. })
  133. b.Run("found", func(b *testing.B) {
  134. lhh := lh.NewRequestHandler()
  135. req := &NebulaMeta{
  136. Type: NebulaMeta_HostQuery,
  137. Details: &NebulaMetaDetails{
  138. OldVpnAddr: 3,
  139. V4AddrPorts: nil,
  140. },
  141. }
  142. p, err := req.Marshal()
  143. require.NoError(b, err)
  144. for n := 0; n < b.N; n++ {
  145. lhh.HandleRequest(rAddr, hi, p, mw)
  146. }
  147. })
  148. }
  149. func TestLighthouse_Memory(t *testing.T) {
  150. l := test.NewLogger()
  151. myUdpAddr0 := netip.MustParseAddrPort("10.0.0.2:4242")
  152. myUdpAddr1 := netip.MustParseAddrPort("192.168.0.2:4242")
  153. myUdpAddr2 := netip.MustParseAddrPort("172.16.0.2:4242")
  154. myUdpAddr3 := netip.MustParseAddrPort("100.152.0.2:4242")
  155. myUdpAddr4 := netip.MustParseAddrPort("24.15.0.2:4242")
  156. myUdpAddr5 := netip.MustParseAddrPort("192.168.0.2:4243")
  157. myUdpAddr6 := netip.MustParseAddrPort("192.168.0.2:4244")
  158. myUdpAddr7 := netip.MustParseAddrPort("192.168.0.2:4245")
  159. myUdpAddr8 := netip.MustParseAddrPort("192.168.0.2:4246")
  160. myUdpAddr9 := netip.MustParseAddrPort("192.168.0.2:4247")
  161. myUdpAddr10 := netip.MustParseAddrPort("192.168.0.2:4248")
  162. myUdpAddr11 := netip.MustParseAddrPort("192.168.0.2:4249")
  163. myVpnIp := netip.MustParseAddr("10.128.0.2")
  164. theirUdpAddr0 := netip.MustParseAddrPort("10.0.0.3:4242")
  165. theirUdpAddr1 := netip.MustParseAddrPort("192.168.0.3:4242")
  166. theirUdpAddr2 := netip.MustParseAddrPort("172.16.0.3:4242")
  167. theirUdpAddr3 := netip.MustParseAddrPort("100.152.0.3:4242")
  168. theirUdpAddr4 := netip.MustParseAddrPort("24.15.0.3:4242")
  169. theirVpnIp := netip.MustParseAddr("10.128.0.3")
  170. c := config.NewC(l)
  171. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  172. c.Settings["listen"] = map[string]any{"port": 4242}
  173. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  174. cs := testCertState(myVpnNet)
  175. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  176. lh.ifce = &mockEncWriter{}
  177. require.NoError(t, err)
  178. lhh := lh.NewRequestHandler()
  179. // Test that my first update responds with just that
  180. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{myUdpAddr1, myUdpAddr2}, lhh)
  181. r := newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  182. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr2)
  183. // Ensure we don't accumulate addresses
  184. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{myUdpAddr3}, lhh)
  185. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  186. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr3)
  187. // Grow it back to 2
  188. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{myUdpAddr1, myUdpAddr4}, lhh)
  189. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  190. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr4)
  191. // Update a different host and ask about it
  192. newLHHostUpdate(theirUdpAddr0, theirVpnIp, []netip.AddrPort{theirUdpAddr1, theirUdpAddr2, theirUdpAddr3, theirUdpAddr4}, lhh)
  193. r = newLHHostRequest(theirUdpAddr0, theirVpnIp, theirVpnIp, lhh)
  194. assertIp4InArray(t, r.msg.Details.V4AddrPorts, theirUdpAddr1, theirUdpAddr2, theirUdpAddr3, theirUdpAddr4)
  195. // Have both hosts ask about the other
  196. r = newLHHostRequest(theirUdpAddr0, theirVpnIp, myVpnIp, lhh)
  197. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr4)
  198. r = newLHHostRequest(myUdpAddr0, myVpnIp, theirVpnIp, lhh)
  199. assertIp4InArray(t, r.msg.Details.V4AddrPorts, theirUdpAddr1, theirUdpAddr2, theirUdpAddr3, theirUdpAddr4)
  200. // Make sure we didn't get changed
  201. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  202. assertIp4InArray(t, r.msg.Details.V4AddrPorts, myUdpAddr1, myUdpAddr4)
  203. // Ensure proper ordering and limiting
  204. // Send 12 addrs, get 10 back, the last 2 removed, allowing the duplicate to remain (clients dedupe)
  205. newLHHostUpdate(
  206. myUdpAddr0,
  207. myVpnIp,
  208. []netip.AddrPort{
  209. myUdpAddr1,
  210. myUdpAddr2,
  211. myUdpAddr3,
  212. myUdpAddr4,
  213. myUdpAddr5,
  214. myUdpAddr5, //Duplicated on purpose
  215. myUdpAddr6,
  216. myUdpAddr7,
  217. myUdpAddr8,
  218. myUdpAddr9,
  219. myUdpAddr10,
  220. myUdpAddr11, // This should get cut
  221. }, lhh)
  222. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  223. assertIp4InArray(
  224. t,
  225. r.msg.Details.V4AddrPorts,
  226. myUdpAddr1, myUdpAddr2, myUdpAddr3, myUdpAddr4, myUdpAddr5, myUdpAddr5, myUdpAddr6, myUdpAddr7, myUdpAddr8, myUdpAddr9,
  227. )
  228. // Make sure we won't add ips in our vpn network
  229. bad1 := netip.MustParseAddrPort("10.128.0.99:4242")
  230. bad2 := netip.MustParseAddrPort("10.128.0.100:4242")
  231. good := netip.MustParseAddrPort("1.128.0.99:4242")
  232. newLHHostUpdate(myUdpAddr0, myVpnIp, []netip.AddrPort{bad1, bad2, good}, lhh)
  233. r = newLHHostRequest(myUdpAddr0, myVpnIp, myVpnIp, lhh)
  234. assertIp4InArray(t, r.msg.Details.V4AddrPorts, good)
  235. }
  236. func TestLighthouse_reload(t *testing.T) {
  237. l := test.NewLogger()
  238. c := config.NewC(l)
  239. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  240. c.Settings["listen"] = map[string]any{"port": 4242}
  241. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  242. cs := testCertState(myVpnNet)
  243. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  244. require.NoError(t, err)
  245. nc := map[string]any{
  246. "static_host_map": map[string]any{
  247. "10.128.0.2": []any{"1.1.1.1:4242"},
  248. },
  249. }
  250. rc, err := yaml.Marshal(nc)
  251. require.NoError(t, err)
  252. c.ReloadConfigString(string(rc))
  253. err = lh.reload(c, false)
  254. require.NoError(t, err)
  255. }
  256. // TestLighthouse_reloadStaticHostMap verifies that reloading static_host_map applies the new
  257. // config rather than appending to it. See issue #718.
  258. func TestLighthouse_reloadStaticHostMap(t *testing.T) {
  259. l := test.NewLogger()
  260. c := config.NewC(l)
  261. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  262. c.Settings["listen"] = map[string]any{"port": 4242}
  263. c.Settings["static_host_map"] = map[string]any{
  264. "10.128.0.2": []any{"1.1.1.1:4242"},
  265. }
  266. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  267. cs := testCertState(myVpnNet)
  268. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  269. require.NoError(t, err)
  270. staticHost := netip.MustParseAddr("10.128.0.2")
  271. otherHost := netip.MustParseAddr("10.128.0.3")
  272. // Capture the RemoteList pointer up front; an in-flight handshake would hold the same one
  273. // on hostinfo.remotes, so it must reflect every reload below.
  274. pinned := lh.Query(staticHost)
  275. require.NotNil(t, pinned)
  276. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("1.1.1.1:4242")}, pinned.CopyAddrs([]netip.Prefix{}))
  277. // Replace the remote address. The new address should be the only entry.
  278. nc := map[string]any{
  279. "static_host_map": map[string]any{
  280. "10.128.0.2": []any{"2.2.2.2:4242"},
  281. },
  282. }
  283. rc, err := yaml.Marshal(nc)
  284. require.NoError(t, err)
  285. require.NoError(t, c.ReloadConfigString(string(rc)))
  286. rl := lh.Query(staticHost)
  287. require.NotNil(t, rl)
  288. assert.Same(t, pinned, rl, "RemoteList pointer must stay stable so in-flight handshakes pick up the change")
  289. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("2.2.2.2:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  290. // Reload back to the original IP. Mirrors the round-trip in issue #718 step 6-8 where
  291. // the buggy reload produced [1.1.1.1, 2.2.2.2, 1.1.1.1] instead of [1.1.1.1].
  292. nc = map[string]any{
  293. "static_host_map": map[string]any{
  294. "10.128.0.2": []any{"1.1.1.1:4242"},
  295. },
  296. }
  297. rc, err = yaml.Marshal(nc)
  298. require.NoError(t, err)
  299. require.NoError(t, c.ReloadConfigString(string(rc)))
  300. rl = lh.Query(staticHost)
  301. require.NotNil(t, rl)
  302. assert.Same(t, pinned, rl)
  303. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("1.1.1.1:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  304. // Reload with the same config. An unchanged entry must not duplicate.
  305. require.NoError(t, c.ReloadConfigString(string(rc)))
  306. rl = lh.Query(staticHost)
  307. require.NotNil(t, rl)
  308. assert.Same(t, pinned, rl)
  309. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("1.1.1.1:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  310. // Switch back to 2.2.2.2 so the rest of the test continues against a known address.
  311. nc = map[string]any{
  312. "static_host_map": map[string]any{
  313. "10.128.0.2": []any{"2.2.2.2:4242"},
  314. },
  315. }
  316. rc, err = yaml.Marshal(nc)
  317. require.NoError(t, err)
  318. require.NoError(t, c.ReloadConfigString(string(rc)))
  319. // Add a second host alongside the first. Both should be present, neither duplicated.
  320. nc = map[string]any{
  321. "static_host_map": map[string]any{
  322. "10.128.0.2": []any{"2.2.2.2:4242"},
  323. "10.128.0.3": []any{"3.3.3.3:4242"},
  324. },
  325. }
  326. rc, err = yaml.Marshal(nc)
  327. require.NoError(t, err)
  328. require.NoError(t, c.ReloadConfigString(string(rc)))
  329. rl = lh.Query(staticHost)
  330. require.NotNil(t, rl)
  331. assert.Same(t, pinned, rl, "adding a sibling entry must not displace the existing RemoteList")
  332. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("2.2.2.2:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  333. rl = lh.Query(otherHost)
  334. require.NotNil(t, rl)
  335. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("3.3.3.3:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  336. // Drop the first host entirely. The vpnAddr is no longer marked static, our owner
  337. // contribution is cleared, but the addrMap entry stays in place so non-static cache
  338. // data (from lighthouse queries) on the same RemoteList isn't lost. In-flight handshakes
  339. // that already had the pointer see an empty address list rather than retrying stale ones.
  340. nc = map[string]any{
  341. "static_host_map": map[string]any{
  342. "10.128.0.3": []any{"3.3.3.3:4242"},
  343. },
  344. }
  345. rc, err = yaml.Marshal(nc)
  346. require.NoError(t, err)
  347. require.NoError(t, c.ReloadConfigString(string(rc)))
  348. _, isStatic := lh.GetStaticHostList()[staticHost]
  349. assert.False(t, isStatic)
  350. rl = lh.Query(staticHost)
  351. require.NotNil(t, rl)
  352. assert.Same(t, pinned, rl)
  353. assert.Empty(t, rl.CopyAddrs([]netip.Prefix{}))
  354. rl = lh.Query(otherHost)
  355. require.NotNil(t, rl)
  356. assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("3.3.3.3:4242")}, rl.CopyAddrs([]netip.Prefix{}))
  357. }
  358. // sendLHHostRequest delivers a HostQuery to lhh and hands back the writer that
  359. // captured what it emitted. Pass a nil filter to see every message.
  360. func sendLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, lhh *LightHouseHandler, filter *NebulaMeta_MessageType) *testEncWriter {
  361. req := &NebulaMeta{
  362. Type: NebulaMeta_HostQuery,
  363. Details: &NebulaMetaDetails{},
  364. }
  365. if queryVpnIp.Is4() {
  366. bip := queryVpnIp.As4()
  367. req.Details.OldVpnAddr = binary.BigEndian.Uint32(bip[:])
  368. } else {
  369. req.Details.VpnAddr = netAddrToProtoAddr(queryVpnIp)
  370. }
  371. b, err := req.Marshal()
  372. if err != nil {
  373. panic(err)
  374. }
  375. w := &testEncWriter{metaFilter: filter}
  376. lhh.HandleRequest(fromAddr, []netip.Addr{myVpnIp}, b, w)
  377. return w
  378. }
  379. func newLHHostRequest(fromAddr netip.AddrPort, myVpnIp, queryVpnIp netip.Addr, lhh *LightHouseHandler) testLhReply {
  380. filter := NebulaMeta_HostQueryReply
  381. return sendLHHostRequest(fromAddr, myVpnIp, queryVpnIp, lhh, &filter).lastReply
  382. }
  383. func TestLighthouse_IgnoresHostQueryForItself(t *testing.T) {
  384. // Validate that we don't answer host queries for our own address.
  385. l := test.NewLogger()
  386. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  387. myVpnIp := myVpnNet.Addr()
  388. c := config.NewC(l)
  389. c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
  390. c.Settings["listen"] = map[string]any{"port": 4242}
  391. // Add a static_host_map entry for ourselves, so our address
  392. // is in the addrMap.
  393. c.Settings["static_host_map"] = map[string]any{
  394. myVpnIp.String(): []any{"192.168.100.1:4242"},
  395. }
  396. lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(myVpnNet), nil, nil)
  397. require.NoError(t, err)
  398. lh.ifce = &mockEncWriter{}
  399. lhh := lh.NewRequestHandler()
  400. peerVpnIp := netip.MustParseAddr("10.128.0.2")
  401. peerUdpAddr := netip.MustParseAddrPort("10.0.0.2:4242")
  402. otherVpnIp := netip.MustParseAddr("10.128.0.3")
  403. otherUdpAddr := netip.MustParseAddrPort("10.0.0.3:4242")
  404. newLHHostUpdate(peerUdpAddr, peerVpnIp, []netip.AddrPort{peerUdpAddr}, lhh)
  405. newLHHostUpdate(otherUdpAddr, otherVpnIp, []netip.AddrPort{otherUdpAddr}, lhh)
  406. // Control: a query about a real peer is still answered, and still ends with
  407. // the punch notification aimed at the host that was asked about.
  408. w := sendLHHostRequest(peerUdpAddr, peerVpnIp, otherVpnIp, lhh, nil)
  409. require.NotNil(t, w.lastReply.msg)
  410. assert.Equal(t, NebulaMeta_HostPunchNotification, w.lastReply.msg.Type)
  411. assert.Equal(t, otherVpnIp, w.lastReply.vpnIp)
  412. // Now validate that we don't send to ourselves.
  413. found, _, err := lh.queryAndPrepMessage(myVpnIp, func(*cache) (int, error) { return 0, nil })
  414. require.NoError(t, err)
  415. require.True(t, found, "the lighthouse should hold a cache entry for its own address")
  416. w = sendLHHostRequest(peerUdpAddr, peerVpnIp, myVpnIp, lhh, nil)
  417. assert.Nil(t, w.lastReply.msg, "a query about our own address must produce no reply and no punch notification")
  418. }
  419. func newLHHostUpdate(fromAddr netip.AddrPort, vpnIp netip.Addr, addrs []netip.AddrPort, lhh *LightHouseHandler) {
  420. req := &NebulaMeta{
  421. Type: NebulaMeta_HostUpdateNotification,
  422. Details: &NebulaMetaDetails{},
  423. }
  424. if vpnIp.Is4() {
  425. bip := vpnIp.As4()
  426. req.Details.OldVpnAddr = binary.BigEndian.Uint32(bip[:])
  427. } else {
  428. req.Details.VpnAddr = netAddrToProtoAddr(vpnIp)
  429. }
  430. for _, v := range addrs {
  431. if v.Addr().Is4() {
  432. req.Details.V4AddrPorts = append(req.Details.V4AddrPorts, netAddrToProtoV4AddrPort(v.Addr(), v.Port()))
  433. } else {
  434. req.Details.V6AddrPorts = append(req.Details.V6AddrPorts, netAddrToProtoV6AddrPort(v.Addr(), v.Port()))
  435. }
  436. }
  437. b, err := req.Marshal()
  438. if err != nil {
  439. panic(err)
  440. }
  441. w := &testEncWriter{}
  442. lhh.HandleRequest(fromAddr, []netip.Addr{vpnIp}, b, w)
  443. }
  444. type testLhReply struct {
  445. nebType header.MessageType
  446. nebSubType header.MessageSubType
  447. vpnIp netip.Addr
  448. msg *NebulaMeta
  449. }
  450. type testEncWriter struct {
  451. lastReply testLhReply
  452. metaFilter *NebulaMeta_MessageType
  453. protocolVersion cert.Version
  454. }
  455. func (tw *testEncWriter) SendVia(via *HostInfo, relay *Relay, ad, nb, out []byte, nocopy bool, q int) {
  456. }
  457. func (tw *testEncWriter) Handshake(vpnIp netip.Addr) {
  458. }
  459. func (tw *testEncWriter) SendMessageToHostInfo(t header.MessageType, st header.MessageSubType, hostinfo *HostInfo, p, _, _ []byte) {
  460. msg := &NebulaMeta{}
  461. err := msg.Unmarshal(p)
  462. if tw.metaFilter == nil || msg.Type == *tw.metaFilter {
  463. tw.lastReply = testLhReply{
  464. nebType: t,
  465. nebSubType: st,
  466. vpnIp: hostinfo.vpnAddrs[0],
  467. msg: msg,
  468. }
  469. }
  470. if err != nil {
  471. panic(err)
  472. }
  473. }
  474. func (tw *testEncWriter) SendMessageToVpnAddr(t header.MessageType, st header.MessageSubType, vpnIp netip.Addr, p, _, _ []byte) {
  475. msg := &NebulaMeta{}
  476. err := msg.Unmarshal(p)
  477. if tw.metaFilter == nil || msg.Type == *tw.metaFilter {
  478. tw.lastReply = testLhReply{
  479. nebType: t,
  480. nebSubType: st,
  481. vpnIp: vpnIp,
  482. msg: msg,
  483. }
  484. }
  485. if err != nil {
  486. panic(err)
  487. }
  488. }
  489. func (tw *testEncWriter) GetHostInfo(vpnIp netip.Addr) *HostInfo {
  490. return nil
  491. }
  492. func (tw *testEncWriter) GetCertState() *CertState {
  493. return &CertState{initiatingVersion: tw.protocolVersion}
  494. }
  495. // assertIp4InArray asserts every address in want is at the same position in have and that the lengths match
  496. func assertIp4InArray(t *testing.T, have []*V4AddrPort, want ...netip.AddrPort) {
  497. if !assert.Len(t, have, len(want)) {
  498. return
  499. }
  500. for k, w := range want {
  501. h := protoV4AddrPortToNetAddrPort(have[k])
  502. if !(h == w) {
  503. assert.Fail(t, fmt.Sprintf("Response did not contain: %v at %v, found %v", w, k, h))
  504. }
  505. }
  506. }
  507. func Test_findNetworkUnion(t *testing.T) {
  508. var out netip.Addr
  509. var ok bool
  510. tenDot := netip.MustParsePrefix("10.0.0.0/8")
  511. oneSevenTwo := netip.MustParsePrefix("172.16.0.0/16")
  512. fe80 := netip.MustParsePrefix("fe80::/8")
  513. fc00 := netip.MustParsePrefix("fc00::/7")
  514. a1 := netip.MustParseAddr("10.0.0.1")
  515. afe81 := netip.MustParseAddr("fe80::1")
  516. //simple
  517. out, ok = findNetworkUnion([]netip.Prefix{tenDot}, []netip.Addr{a1})
  518. assert.True(t, ok)
  519. assert.Equal(t, out, a1)
  520. //mixed lengths
  521. out, ok = findNetworkUnion([]netip.Prefix{tenDot}, []netip.Addr{a1, afe81})
  522. assert.True(t, ok)
  523. assert.Equal(t, out, a1)
  524. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo}, []netip.Addr{a1})
  525. assert.True(t, ok)
  526. assert.Equal(t, out, a1)
  527. //mixed family
  528. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{a1})
  529. assert.True(t, ok)
  530. assert.Equal(t, out, a1)
  531. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{a1, afe81})
  532. assert.True(t, ok)
  533. assert.Equal(t, out, a1)
  534. //ordering
  535. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{afe81, a1})
  536. assert.True(t, ok)
  537. assert.Equal(t, out, a1)
  538. out, ok = findNetworkUnion([]netip.Prefix{fe80, tenDot, oneSevenTwo}, []netip.Addr{afe81, a1})
  539. assert.True(t, ok)
  540. assert.Equal(t, out, afe81)
  541. //some mismatches
  542. out, ok = findNetworkUnion([]netip.Prefix{tenDot, oneSevenTwo, fe80}, []netip.Addr{afe81})
  543. assert.True(t, ok)
  544. assert.Equal(t, out, afe81)
  545. out, ok = findNetworkUnion([]netip.Prefix{oneSevenTwo, fe80}, []netip.Addr{a1, afe81})
  546. assert.True(t, ok)
  547. assert.Equal(t, out, afe81)
  548. //falsey cases
  549. out, ok = findNetworkUnion([]netip.Prefix{oneSevenTwo, fe80}, []netip.Addr{a1})
  550. assert.False(t, ok)
  551. out, ok = findNetworkUnion([]netip.Prefix{fc00, fe80}, []netip.Addr{a1})
  552. assert.False(t, ok)
  553. out, ok = findNetworkUnion([]netip.Prefix{oneSevenTwo, fc00}, []netip.Addr{a1, afe81})
  554. assert.False(t, ok)
  555. out, ok = findNetworkUnion([]netip.Prefix{fc00}, []netip.Addr{a1, afe81})
  556. assert.False(t, ok)
  557. }
  558. func TestLighthouse_Dont_Delete_Static_Hosts(t *testing.T) {
  559. l := test.NewLogger()
  560. myUdpAddr2 := netip.MustParseAddrPort("1.2.3.4:4242")
  561. testSameHostNotStatic := netip.MustParseAddr("10.128.0.41")
  562. testStaticHost := netip.MustParseAddr("10.128.0.42")
  563. //myVpnIp := netip.MustParseAddr("10.128.0.2")
  564. c := config.NewC(l)
  565. lh1 := "10.128.0.2"
  566. c.Settings["lighthouse"] = map[string]any{
  567. "hosts": []any{lh1},
  568. "interval": "1s",
  569. }
  570. c.Settings["listen"] = map[string]any{"port": 4242}
  571. c.Settings["static_host_map"] = map[string]any{
  572. lh1: []any{"1.1.1.1:4242"},
  573. "10.128.0.42": []any{"1.2.3.4:4242"},
  574. }
  575. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  576. cs := testCertState(myVpnNet)
  577. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  578. require.NoError(t, err)
  579. lh.ifce = &mockEncWriter{}
  580. //test that we actually have the static entry:
  581. out := lh.Query(testStaticHost)
  582. assert.NotNil(t, out)
  583. assert.Equal(t, out.vpnAddrs[0], testStaticHost)
  584. out.Rebuild([]netip.Prefix{}) //why tho
  585. assert.Equal(t, out.addrs[0], myUdpAddr2)
  586. //bolt on a lower numbered primary IP
  587. am := lh.unlockedGetRemoteList([]netip.Addr{testStaticHost})
  588. am.vpnAddrs = []netip.Addr{testSameHostNotStatic, testStaticHost}
  589. lh.addrMap[testSameHostNotStatic] = am
  590. out.Rebuild([]netip.Prefix{}) //???
  591. //test that we actually have the static entry:
  592. out = lh.Query(testStaticHost)
  593. assert.NotNil(t, out)
  594. assert.Equal(t, out.vpnAddrs[0], testSameHostNotStatic)
  595. assert.Equal(t, out.vpnAddrs[1], testStaticHost)
  596. assert.Equal(t, out.addrs[0], myUdpAddr2)
  597. //test that we actually have the static entry for BOTH:
  598. out2 := lh.Query(testSameHostNotStatic)
  599. assert.Same(t, out2, out)
  600. //now do the delete
  601. lh.DeleteVpnAddrs([]netip.Addr{testSameHostNotStatic, testStaticHost})
  602. //verify
  603. out = lh.Query(testSameHostNotStatic)
  604. assert.NotNil(t, out)
  605. if out == nil {
  606. t.Fatal("expected non-nil query for the static host")
  607. }
  608. assert.Equal(t, out.vpnAddrs[0], testSameHostNotStatic)
  609. assert.Equal(t, out.vpnAddrs[1], testStaticHost)
  610. assert.Equal(t, out.addrs[0], myUdpAddr2)
  611. }
  612. func TestLighthouse_DeletesWork(t *testing.T) {
  613. l := test.NewLogger()
  614. myUdpAddr2 := netip.MustParseAddrPort("1.2.3.4:4242")
  615. testHost := netip.MustParseAddr("10.128.0.42")
  616. c := config.NewC(l)
  617. lh1 := "10.128.0.2"
  618. c.Settings["lighthouse"] = map[string]any{
  619. "hosts": []any{lh1},
  620. "interval": "1s",
  621. }
  622. c.Settings["listen"] = map[string]any{"port": 4242}
  623. c.Settings["static_host_map"] = map[string]any{
  624. lh1: []any{"1.1.1.1:4242"},
  625. }
  626. myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
  627. cs := testCertState(myVpnNet)
  628. lh, err := NewLightHouseFromConfig(t.Context(), l, c, cs, nil, nil)
  629. require.NoError(t, err)
  630. lh.ifce = &mockEncWriter{}
  631. //insert the host
  632. am := lh.unlockedGetRemoteList([]netip.Addr{testHost})
  633. am.vpnAddrs = []netip.Addr{testHost}
  634. am.addrs = []netip.AddrPort{myUdpAddr2}
  635. lh.addrMap[testHost] = am
  636. am.Rebuild([]netip.Prefix{}) //???
  637. //test that we actually have the entry:
  638. out := lh.Query(testHost)
  639. assert.NotNil(t, out)
  640. assert.Equal(t, out.vpnAddrs[0], testHost)
  641. out.Rebuild([]netip.Prefix{}) //why tho
  642. assert.Equal(t, out.addrs[0], myUdpAddr2)
  643. //now do the delete
  644. lh.DeleteVpnAddrs([]netip.Addr{testHost})
  645. //verify
  646. out = lh.Query(testHost)
  647. assert.Nil(t, out)
  648. }
  649. func TestLightHouse_logLocalAddrsErr(t *testing.T) {
  650. out := &bytes.Buffer{}
  651. lh := &LightHouse{l: test.NewLoggerWithOutput(out)}
  652. // The first sighting of a failure warns.
  653. lh.logLocalAddrsErr(errors.New("permission denied"))
  654. assert.Contains(t, out.String(), "level=WARN")
  655. assert.Contains(t, out.String(), "permission denied")
  656. // Repeating unchanged does not warn again, which is what keeps a permanent failure from warning
  657. // on every lighthouse.interval for the life of the process.
  658. out.Reset()
  659. lh.logLocalAddrsErr(errors.New("permission denied"))
  660. assert.NotContains(t, out.String(), "level=WARN")
  661. // A different failure is a new event and warns.
  662. out.Reset()
  663. lh.logLocalAddrsErr(errors.New("something else"))
  664. assert.Contains(t, out.String(), "level=WARN")
  665. assert.Contains(t, out.String(), "something else")
  666. // Recovering resets, so the same failure returning later warns again.
  667. out.Reset()
  668. lh.logLocalAddrsErr(nil)
  669. assert.Empty(t, out.String())
  670. lh.logLocalAddrsErr(errors.New("something else"))
  671. assert.Contains(t, out.String(), "level=WARN")
  672. }