lighthouse_test.go 27 KB

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