connection_manager_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package nebula
  2. import (
  3. "crypto/ed25519"
  4. "crypto/rand"
  5. "net"
  6. "net/netip"
  7. "testing"
  8. "time"
  9. "github.com/flynn/noise"
  10. "github.com/slackhq/nebula/cert"
  11. "github.com/slackhq/nebula/config"
  12. "github.com/slackhq/nebula/test"
  13. "github.com/slackhq/nebula/udp"
  14. "github.com/stretchr/testify/assert"
  15. )
  16. func newTestLighthouse() *LightHouse {
  17. lh := &LightHouse{
  18. l: test.NewLogger(),
  19. addrMap: map[netip.Addr]*RemoteList{},
  20. queryChan: make(chan netip.Addr, 10),
  21. }
  22. lighthouses := map[netip.Addr]struct{}{}
  23. staticList := map[netip.Addr]struct{}{}
  24. lh.lighthouses.Store(&lighthouses)
  25. lh.staticList.Store(&staticList)
  26. return lh
  27. }
  28. func Test_NewConnectionManagerTest(t *testing.T) {
  29. l := test.NewLogger()
  30. //_, tuncidr, _ := net.ParseCIDR("1.1.1.1/24")
  31. vpncidr := netip.MustParsePrefix("172.1.1.1/24")
  32. localrange := netip.MustParsePrefix("10.1.1.1/24")
  33. vpnIp := netip.MustParseAddr("172.1.1.2")
  34. preferredRanges := []netip.Prefix{localrange}
  35. // Very incomplete mock objects
  36. hostMap := newHostMap(l, vpncidr)
  37. hostMap.preferredRanges.Store(&preferredRanges)
  38. cs := &CertState{
  39. RawCertificate: []byte{},
  40. PrivateKey: []byte{},
  41. Certificate: &cert.NebulaCertificate{},
  42. RawCertificateNoKey: []byte{},
  43. }
  44. lh := newTestLighthouse()
  45. ifce := &Interface{
  46. hostMap: hostMap,
  47. inside: &test.NoopTun{},
  48. outside: &udp.NoopConn{},
  49. firewall: &Firewall{},
  50. lightHouse: lh,
  51. pki: &PKI{},
  52. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  53. l: l,
  54. }
  55. ifce.pki.cs.Store(cs)
  56. // Create manager
  57. conf := config.NewC(l)
  58. punchy := NewPunchyFromConfig(l, conf)
  59. nc := newConnectionManagerFromConfig(l, conf, hostMap, punchy)
  60. nc.intf = ifce
  61. p := []byte("")
  62. nb := make([]byte, 12, 12)
  63. out := make([]byte, mtu)
  64. // Add an ip we have established a connection w/ to hostmap
  65. hostinfo := &HostInfo{
  66. vpnIp: vpnIp,
  67. localIndexId: 1099,
  68. remoteIndexId: 9901,
  69. }
  70. hostinfo.ConnectionState = &ConnectionState{
  71. myCert: &cert.NebulaCertificate{},
  72. H: &noise.HandshakeState{},
  73. }
  74. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  75. // We saw traffic out to vpnIp
  76. nc.Out(hostinfo)
  77. nc.In(hostinfo)
  78. assert.False(t, hostinfo.pendingDeletion.Load())
  79. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  80. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  81. assert.True(t, hostinfo.out.Load())
  82. assert.True(t, hostinfo.in.Load())
  83. // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded
  84. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  85. assert.False(t, hostinfo.pendingDeletion.Load())
  86. assert.False(t, hostinfo.out.Load())
  87. assert.False(t, hostinfo.in.Load())
  88. // Do another traffic check tick, this host should be pending deletion now
  89. nc.Out(hostinfo)
  90. assert.True(t, hostinfo.out.Load())
  91. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  92. assert.True(t, hostinfo.pendingDeletion.Load())
  93. assert.False(t, hostinfo.out.Load())
  94. assert.False(t, hostinfo.in.Load())
  95. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  96. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  97. // Do a final traffic check tick, the host should now be removed
  98. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  99. assert.NotContains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  100. assert.NotContains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  101. }
  102. func Test_NewConnectionManagerTest2(t *testing.T) {
  103. l := test.NewLogger()
  104. //_, tuncidr, _ := net.ParseCIDR("1.1.1.1/24")
  105. vpncidr := netip.MustParsePrefix("172.1.1.1/24")
  106. localrange := netip.MustParsePrefix("10.1.1.1/24")
  107. vpnIp := netip.MustParseAddr("172.1.1.2")
  108. preferredRanges := []netip.Prefix{localrange}
  109. // Very incomplete mock objects
  110. hostMap := newHostMap(l, vpncidr)
  111. hostMap.preferredRanges.Store(&preferredRanges)
  112. cs := &CertState{
  113. RawCertificate: []byte{},
  114. PrivateKey: []byte{},
  115. Certificate: &cert.NebulaCertificate{},
  116. RawCertificateNoKey: []byte{},
  117. }
  118. lh := newTestLighthouse()
  119. ifce := &Interface{
  120. hostMap: hostMap,
  121. inside: &test.NoopTun{},
  122. outside: &udp.NoopConn{},
  123. firewall: &Firewall{},
  124. lightHouse: lh,
  125. pki: &PKI{},
  126. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  127. l: l,
  128. }
  129. ifce.pki.cs.Store(cs)
  130. // Create manager
  131. conf := config.NewC(l)
  132. punchy := NewPunchyFromConfig(l, conf)
  133. nc := newConnectionManagerFromConfig(l, conf, hostMap, punchy)
  134. nc.intf = ifce
  135. p := []byte("")
  136. nb := make([]byte, 12, 12)
  137. out := make([]byte, mtu)
  138. // Add an ip we have established a connection w/ to hostmap
  139. hostinfo := &HostInfo{
  140. vpnIp: vpnIp,
  141. localIndexId: 1099,
  142. remoteIndexId: 9901,
  143. }
  144. hostinfo.ConnectionState = &ConnectionState{
  145. myCert: &cert.NebulaCertificate{},
  146. H: &noise.HandshakeState{},
  147. }
  148. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  149. // We saw traffic out to vpnIp
  150. nc.Out(hostinfo)
  151. nc.In(hostinfo)
  152. assert.True(t, hostinfo.in.Load())
  153. assert.True(t, hostinfo.out.Load())
  154. assert.False(t, hostinfo.pendingDeletion.Load())
  155. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  156. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  157. // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded
  158. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  159. assert.False(t, hostinfo.pendingDeletion.Load())
  160. assert.False(t, hostinfo.out.Load())
  161. assert.False(t, hostinfo.in.Load())
  162. // Do another traffic check tick, this host should be pending deletion now
  163. nc.Out(hostinfo)
  164. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  165. assert.True(t, hostinfo.pendingDeletion.Load())
  166. assert.False(t, hostinfo.out.Load())
  167. assert.False(t, hostinfo.in.Load())
  168. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  169. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  170. // We saw traffic, should no longer be pending deletion
  171. nc.In(hostinfo)
  172. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  173. assert.False(t, hostinfo.pendingDeletion.Load())
  174. assert.False(t, hostinfo.out.Load())
  175. assert.False(t, hostinfo.in.Load())
  176. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  177. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  178. }
  179. func Test_NewConnectionManager_DisconnectInactive(t *testing.T) {
  180. l := test.NewLogger()
  181. vpncidr := netip.MustParsePrefix("172.1.1.1/24")
  182. localrange := netip.MustParsePrefix("10.1.1.1/24")
  183. vpnIp := netip.MustParseAddr("172.1.1.2")
  184. preferredRanges := []netip.Prefix{localrange}
  185. // Very incomplete mock objects
  186. hostMap := newHostMap(l, vpncidr)
  187. hostMap.preferredRanges.Store(&preferredRanges)
  188. cs := &CertState{
  189. RawCertificate: []byte{},
  190. PrivateKey: []byte{},
  191. Certificate: &cert.NebulaCertificate{},
  192. RawCertificateNoKey: []byte{},
  193. }
  194. lh := newTestLighthouse()
  195. ifce := &Interface{
  196. hostMap: hostMap,
  197. inside: &test.NoopTun{},
  198. outside: &udp.NoopConn{},
  199. firewall: &Firewall{},
  200. lightHouse: lh,
  201. pki: &PKI{},
  202. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  203. l: l,
  204. }
  205. ifce.pki.cs.Store(cs)
  206. // Create manager
  207. conf := config.NewC(l)
  208. conf.Settings["tunnels"] = map[interface{}]interface{}{
  209. "drop_inactive": true,
  210. }
  211. punchy := NewPunchyFromConfig(l, conf)
  212. nc := newConnectionManagerFromConfig(l, conf, hostMap, punchy)
  213. assert.True(t, nc.dropInactive.Load())
  214. nc.intf = ifce
  215. // Add an ip we have established a connection w/ to hostmap
  216. hostinfo := &HostInfo{
  217. vpnIp: vpnIp,
  218. localIndexId: 1099,
  219. remoteIndexId: 9901,
  220. }
  221. hostinfo.ConnectionState = &ConnectionState{
  222. myCert: &cert.NebulaCertificate{},
  223. H: &noise.HandshakeState{},
  224. }
  225. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  226. // Do a traffic check tick, in and out should be cleared but should not be pending deletion
  227. nc.Out(hostinfo)
  228. nc.In(hostinfo)
  229. assert.True(t, hostinfo.out.Load())
  230. assert.True(t, hostinfo.in.Load())
  231. now := time.Now()
  232. decision, _, _ := nc.makeTrafficDecision(hostinfo.localIndexId, now)
  233. assert.Equal(t, tryRehandshake, decision)
  234. assert.Equal(t, now, hostinfo.lastUsed)
  235. assert.False(t, hostinfo.pendingDeletion.Load())
  236. assert.False(t, hostinfo.out.Load())
  237. assert.False(t, hostinfo.in.Load())
  238. decision, _, _ = nc.makeTrafficDecision(hostinfo.localIndexId, now.Add(time.Second*5))
  239. assert.Equal(t, doNothing, decision)
  240. assert.Equal(t, now, hostinfo.lastUsed)
  241. assert.False(t, hostinfo.pendingDeletion.Load())
  242. assert.False(t, hostinfo.out.Load())
  243. assert.False(t, hostinfo.in.Load())
  244. // Do another traffic check tick, should still not be pending deletion
  245. decision, _, _ = nc.makeTrafficDecision(hostinfo.localIndexId, now.Add(time.Second*10))
  246. assert.Equal(t, doNothing, decision)
  247. assert.Equal(t, now, hostinfo.lastUsed)
  248. assert.False(t, hostinfo.pendingDeletion.Load())
  249. assert.False(t, hostinfo.out.Load())
  250. assert.False(t, hostinfo.in.Load())
  251. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  252. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  253. // Finally advance beyond the inactivity timeout
  254. decision, _, _ = nc.makeTrafficDecision(hostinfo.localIndexId, now.Add(time.Minute*10))
  255. assert.Equal(t, closeTunnel, decision)
  256. assert.Equal(t, now, hostinfo.lastUsed)
  257. assert.False(t, hostinfo.pendingDeletion.Load())
  258. assert.False(t, hostinfo.out.Load())
  259. assert.False(t, hostinfo.in.Load())
  260. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  261. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  262. }
  263. // Check if we can disconnect the peer.
  264. // Validate if the peer's certificate is invalid (expired, etc.)
  265. // Disconnect only if disconnectInvalid: true is set.
  266. func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) {
  267. now := time.Now()
  268. l := test.NewLogger()
  269. ipNet := net.IPNet{
  270. IP: net.IPv4(172, 1, 1, 2),
  271. Mask: net.IPMask{255, 255, 255, 0},
  272. }
  273. vpncidr := netip.MustParsePrefix("172.1.1.1/24")
  274. localrange := netip.MustParsePrefix("10.1.1.1/24")
  275. vpnIp := netip.MustParseAddr("172.1.1.2")
  276. preferredRanges := []netip.Prefix{localrange}
  277. hostMap := newHostMap(l, vpncidr)
  278. hostMap.preferredRanges.Store(&preferredRanges)
  279. // Generate keys for CA and peer's cert.
  280. pubCA, privCA, _ := ed25519.GenerateKey(rand.Reader)
  281. caCert := cert.NebulaCertificate{
  282. Details: cert.NebulaCertificateDetails{
  283. Name: "ca",
  284. NotBefore: now,
  285. NotAfter: now.Add(1 * time.Hour),
  286. IsCA: true,
  287. PublicKey: pubCA,
  288. },
  289. }
  290. assert.NoError(t, caCert.Sign(cert.Curve_CURVE25519, privCA))
  291. ncp := &cert.NebulaCAPool{
  292. CAs: cert.NewCAPool().CAs,
  293. }
  294. ncp.CAs["ca"] = &caCert
  295. pubCrt, _, _ := ed25519.GenerateKey(rand.Reader)
  296. peerCert := cert.NebulaCertificate{
  297. Details: cert.NebulaCertificateDetails{
  298. Name: "host",
  299. Ips: []*net.IPNet{&ipNet},
  300. Subnets: []*net.IPNet{},
  301. NotBefore: now,
  302. NotAfter: now.Add(60 * time.Second),
  303. PublicKey: pubCrt,
  304. IsCA: false,
  305. Issuer: "ca",
  306. },
  307. }
  308. assert.NoError(t, peerCert.Sign(cert.Curve_CURVE25519, privCA))
  309. cs := &CertState{
  310. RawCertificate: []byte{},
  311. PrivateKey: []byte{},
  312. Certificate: &cert.NebulaCertificate{},
  313. RawCertificateNoKey: []byte{},
  314. }
  315. lh := newTestLighthouse()
  316. ifce := &Interface{
  317. hostMap: hostMap,
  318. inside: &test.NoopTun{},
  319. outside: &udp.NoopConn{},
  320. firewall: &Firewall{},
  321. lightHouse: lh,
  322. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  323. l: l,
  324. pki: &PKI{},
  325. }
  326. ifce.pki.cs.Store(cs)
  327. ifce.pki.caPool.Store(ncp)
  328. ifce.disconnectInvalid.Store(true)
  329. // Create manager
  330. conf := config.NewC(l)
  331. punchy := NewPunchyFromConfig(l, conf)
  332. nc := newConnectionManagerFromConfig(l, conf, hostMap, punchy)
  333. nc.intf = ifce
  334. ifce.connectionManager = nc
  335. hostinfo := &HostInfo{
  336. vpnIp: vpnIp,
  337. ConnectionState: &ConnectionState{
  338. myCert: &cert.NebulaCertificate{},
  339. peerCert: &peerCert,
  340. H: &noise.HandshakeState{},
  341. },
  342. }
  343. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  344. // Move ahead 45s.
  345. // Check if to disconnect with invalid certificate.
  346. // Should be alive.
  347. nextTick := now.Add(45 * time.Second)
  348. invalid := nc.isInvalidCertificate(nextTick, hostinfo)
  349. assert.False(t, invalid)
  350. // Move ahead 61s.
  351. // Check if to disconnect with invalid certificate.
  352. // Should be disconnected.
  353. nextTick = now.Add(61 * time.Second)
  354. invalid = nc.isInvalidCertificate(nextTick, hostinfo)
  355. assert.True(t, invalid)
  356. }