connection_manager_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. package nebula
  2. import (
  3. "crypto/ed25519"
  4. "crypto/rand"
  5. "net/netip"
  6. "testing"
  7. "time"
  8. "github.com/slackhq/nebula/cert"
  9. "github.com/slackhq/nebula/config"
  10. "github.com/slackhq/nebula/overlay/overlaytest"
  11. "github.com/slackhq/nebula/test"
  12. "github.com/slackhq/nebula/udp"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  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 := []netip.Addr{}
  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. localrange := netip.MustParsePrefix("10.1.1.1/24")
  32. vpnIp := netip.MustParseAddr("172.1.1.2")
  33. preferredRanges := []netip.Prefix{localrange}
  34. // Very incomplete mock objects
  35. hostMap := newHostMap(l)
  36. hostMap.preferredRanges.Store(&preferredRanges)
  37. cs := &CertState{
  38. initiatingVersion: cert.Version1,
  39. privateKey: []byte{},
  40. v1Cert: &dummyCert{version: cert.Version1},
  41. v1Credential: nil,
  42. }
  43. lh := newTestLighthouse()
  44. ifce := &Interface{
  45. hostMap: hostMap,
  46. inside: &overlaytest.NoopTun{},
  47. outside: &udp.NoopConn{},
  48. firewall: &Firewall{},
  49. lightHouse: lh,
  50. pki: &PKI{},
  51. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  52. l: l,
  53. }
  54. ifce.pki.cs.Store(cs)
  55. // Create manager
  56. conf := config.NewC(test.NewLogger())
  57. punchy := NewPunchyFromConfig(test.NewLogger(), conf)
  58. nc := newConnectionManagerFromConfig(test.NewLogger(), conf, hostMap, punchy)
  59. nc.intf = ifce
  60. p := []byte("")
  61. buf := NewWireBuffer(mtu, 0)
  62. // Add an ip we have established a connection w/ to hostmap
  63. hostinfo := &HostInfo{
  64. vpnAddrs: []netip.Addr{vpnIp},
  65. localIndexId: 1099,
  66. remoteIndexId: 9901,
  67. }
  68. hostinfo.ConnectionState = &ConnectionState{
  69. myCert: &dummyCert{version: cert.Version1},
  70. }
  71. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  72. // We saw traffic out to vpnIp
  73. nc.Out(hostinfo)
  74. nc.In(hostinfo)
  75. assert.False(t, hostinfo.pendingDeletion.Load())
  76. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs[0])
  77. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  78. assert.True(t, hostinfo.out.Load())
  79. assert.True(t, hostinfo.in.Load())
  80. // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded
  81. nc.doTrafficCheck(hostinfo.localIndexId, p, buf, time.Now())
  82. assert.False(t, hostinfo.pendingDeletion.Load())
  83. assert.False(t, hostinfo.out.Load())
  84. assert.False(t, hostinfo.in.Load())
  85. // Do another traffic check tick, this host should be pending deletion now
  86. nc.Out(hostinfo)
  87. assert.True(t, hostinfo.out.Load())
  88. nc.doTrafficCheck(hostinfo.localIndexId, p, buf, time.Now())
  89. assert.True(t, hostinfo.pendingDeletion.Load())
  90. assert.False(t, hostinfo.out.Load())
  91. assert.False(t, hostinfo.in.Load())
  92. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  93. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs[0])
  94. // Do a final traffic check tick, the host should now be removed
  95. nc.doTrafficCheck(hostinfo.localIndexId, p, buf, time.Now())
  96. assert.NotContains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs)
  97. assert.NotContains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  98. }
  99. func Test_NewConnectionManagerTest2(t *testing.T) {
  100. l := test.NewLogger()
  101. //_, tuncidr, _ := net.ParseCIDR("1.1.1.1/24")
  102. localrange := netip.MustParsePrefix("10.1.1.1/24")
  103. vpnIp := netip.MustParseAddr("172.1.1.2")
  104. preferredRanges := []netip.Prefix{localrange}
  105. // Very incomplete mock objects
  106. hostMap := newHostMap(l)
  107. hostMap.preferredRanges.Store(&preferredRanges)
  108. cs := &CertState{
  109. initiatingVersion: cert.Version1,
  110. privateKey: []byte{},
  111. v1Cert: &dummyCert{version: cert.Version1},
  112. v1Credential: nil,
  113. }
  114. lh := newTestLighthouse()
  115. ifce := &Interface{
  116. hostMap: hostMap,
  117. inside: &overlaytest.NoopTun{},
  118. outside: &udp.NoopConn{},
  119. firewall: &Firewall{},
  120. lightHouse: lh,
  121. pki: &PKI{},
  122. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  123. l: l,
  124. }
  125. ifce.pki.cs.Store(cs)
  126. // Create manager
  127. conf := config.NewC(test.NewLogger())
  128. punchy := NewPunchyFromConfig(test.NewLogger(), conf)
  129. nc := newConnectionManagerFromConfig(test.NewLogger(), conf, hostMap, punchy)
  130. nc.intf = ifce
  131. p := []byte("")
  132. buf := NewWireBuffer(mtu, 0)
  133. // Add an ip we have established a connection w/ to hostmap
  134. hostinfo := &HostInfo{
  135. vpnAddrs: []netip.Addr{vpnIp},
  136. localIndexId: 1099,
  137. remoteIndexId: 9901,
  138. }
  139. hostinfo.ConnectionState = &ConnectionState{
  140. myCert: &dummyCert{version: cert.Version1},
  141. }
  142. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  143. // We saw traffic out to vpnIp
  144. nc.Out(hostinfo)
  145. nc.In(hostinfo)
  146. assert.True(t, hostinfo.in.Load())
  147. assert.True(t, hostinfo.out.Load())
  148. assert.False(t, hostinfo.pendingDeletion.Load())
  149. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs[0])
  150. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  151. // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded
  152. nc.doTrafficCheck(hostinfo.localIndexId, p, buf, time.Now())
  153. assert.False(t, hostinfo.pendingDeletion.Load())
  154. assert.False(t, hostinfo.out.Load())
  155. assert.False(t, hostinfo.in.Load())
  156. // Do another traffic check tick, this host should be pending deletion now
  157. nc.Out(hostinfo)
  158. nc.doTrafficCheck(hostinfo.localIndexId, p, buf, time.Now())
  159. assert.True(t, hostinfo.pendingDeletion.Load())
  160. assert.False(t, hostinfo.out.Load())
  161. assert.False(t, hostinfo.in.Load())
  162. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  163. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs[0])
  164. // We saw traffic, should no longer be pending deletion
  165. nc.In(hostinfo)
  166. nc.doTrafficCheck(hostinfo.localIndexId, p, buf, time.Now())
  167. assert.False(t, hostinfo.pendingDeletion.Load())
  168. assert.False(t, hostinfo.out.Load())
  169. assert.False(t, hostinfo.in.Load())
  170. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  171. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs[0])
  172. }
  173. func Test_NewConnectionManager_DisconnectInactive(t *testing.T) {
  174. l := test.NewLogger()
  175. localrange := netip.MustParsePrefix("10.1.1.1/24")
  176. vpnAddrs := []netip.Addr{netip.MustParseAddr("172.1.1.2")}
  177. preferredRanges := []netip.Prefix{localrange}
  178. // Very incomplete mock objects
  179. hostMap := newHostMap(l)
  180. hostMap.preferredRanges.Store(&preferredRanges)
  181. cs := &CertState{
  182. initiatingVersion: cert.Version1,
  183. privateKey: []byte{},
  184. v1Cert: &dummyCert{version: cert.Version1},
  185. v1Credential: nil,
  186. }
  187. lh := newTestLighthouse()
  188. ifce := &Interface{
  189. hostMap: hostMap,
  190. inside: &overlaytest.NoopTun{},
  191. outside: &udp.NoopConn{},
  192. firewall: &Firewall{},
  193. lightHouse: lh,
  194. pki: &PKI{},
  195. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  196. l: l,
  197. }
  198. ifce.pki.cs.Store(cs)
  199. // Create manager
  200. conf := config.NewC(test.NewLogger())
  201. conf.Settings["tunnels"] = map[string]any{
  202. "drop_inactive": true,
  203. }
  204. punchy := NewPunchyFromConfig(test.NewLogger(), conf)
  205. nc := newConnectionManagerFromConfig(test.NewLogger(), conf, hostMap, punchy)
  206. assert.True(t, nc.dropInactive.Load())
  207. nc.intf = ifce
  208. // Add an ip we have established a connection w/ to hostmap
  209. hostinfo := &HostInfo{
  210. vpnAddrs: vpnAddrs,
  211. localIndexId: 1099,
  212. remoteIndexId: 9901,
  213. }
  214. hostinfo.ConnectionState = &ConnectionState{
  215. myCert: &dummyCert{version: cert.Version1},
  216. }
  217. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  218. // Do a traffic check tick, in and out should be cleared but should not be pending deletion
  219. nc.Out(hostinfo)
  220. nc.In(hostinfo)
  221. assert.True(t, hostinfo.out.Load())
  222. assert.True(t, hostinfo.in.Load())
  223. now := time.Now()
  224. decision, _, _ := nc.makeTrafficDecision(hostinfo.localIndexId, now)
  225. assert.Equal(t, tryRehandshake, decision)
  226. assert.Equal(t, now, hostinfo.lastUsed)
  227. assert.False(t, hostinfo.pendingDeletion.Load())
  228. assert.False(t, hostinfo.out.Load())
  229. assert.False(t, hostinfo.in.Load())
  230. decision, _, _ = nc.makeTrafficDecision(hostinfo.localIndexId, now.Add(time.Second*5))
  231. assert.Equal(t, doNothing, decision)
  232. assert.Equal(t, now, hostinfo.lastUsed)
  233. assert.False(t, hostinfo.pendingDeletion.Load())
  234. assert.False(t, hostinfo.out.Load())
  235. assert.False(t, hostinfo.in.Load())
  236. // Do another traffic check tick, should still not be pending deletion
  237. decision, _, _ = nc.makeTrafficDecision(hostinfo.localIndexId, now.Add(time.Second*10))
  238. assert.Equal(t, doNothing, decision)
  239. assert.Equal(t, now, hostinfo.lastUsed)
  240. assert.False(t, hostinfo.pendingDeletion.Load())
  241. assert.False(t, hostinfo.out.Load())
  242. assert.False(t, hostinfo.in.Load())
  243. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  244. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs[0])
  245. // Finally advance beyond the inactivity timeout
  246. decision, _, _ = nc.makeTrafficDecision(hostinfo.localIndexId, now.Add(time.Minute*10))
  247. assert.Equal(t, closeTunnel, decision)
  248. assert.Equal(t, now, hostinfo.lastUsed)
  249. assert.False(t, hostinfo.pendingDeletion.Load())
  250. assert.False(t, hostinfo.out.Load())
  251. assert.False(t, hostinfo.in.Load())
  252. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  253. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnAddrs[0])
  254. }
  255. // Check if we can disconnect the peer.
  256. // Validate if the peer's certificate is invalid (expired, etc.)
  257. // Disconnect only if disconnectInvalid: true is set.
  258. func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) {
  259. now := time.Now()
  260. l := test.NewLogger()
  261. vpncidr := netip.MustParsePrefix("172.1.1.1/24")
  262. localrange := netip.MustParsePrefix("10.1.1.1/24")
  263. vpnIp := netip.MustParseAddr("172.1.1.2")
  264. preferredRanges := []netip.Prefix{localrange}
  265. hostMap := newHostMap(l)
  266. hostMap.preferredRanges.Store(&preferredRanges)
  267. // Generate keys for CA and peer's cert.
  268. pubCA, privCA, _ := ed25519.GenerateKey(rand.Reader)
  269. tbs := &cert.TBSCertificate{
  270. Version: 1,
  271. Name: "ca",
  272. IsCA: true,
  273. NotBefore: now,
  274. NotAfter: now.Add(1 * time.Hour),
  275. PublicKey: pubCA,
  276. }
  277. caCert, err := tbs.Sign(nil, cert.Curve_CURVE25519, privCA)
  278. require.NoError(t, err)
  279. ncp := cert.NewCAPool()
  280. require.NoError(t, ncp.AddCA(caCert))
  281. pubCrt, _, _ := ed25519.GenerateKey(rand.Reader)
  282. tbs = &cert.TBSCertificate{
  283. Version: 1,
  284. Name: "host",
  285. Networks: []netip.Prefix{vpncidr},
  286. NotBefore: now,
  287. NotAfter: now.Add(60 * time.Second),
  288. PublicKey: pubCrt,
  289. }
  290. peerCert, err := tbs.Sign(caCert, cert.Curve_CURVE25519, privCA)
  291. require.NoError(t, err)
  292. cachedPeerCert, err := ncp.VerifyCertificate(now.Add(time.Second), peerCert)
  293. cs := &CertState{
  294. privateKey: []byte{},
  295. v1Cert: &dummyCert{},
  296. v1Credential: nil,
  297. }
  298. lh := newTestLighthouse()
  299. ifce := &Interface{
  300. hostMap: hostMap,
  301. inside: &overlaytest.NoopTun{},
  302. outside: &udp.NoopConn{},
  303. firewall: &Firewall{},
  304. lightHouse: lh,
  305. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  306. l: l,
  307. pki: &PKI{},
  308. }
  309. ifce.pki.cs.Store(cs)
  310. ifce.pki.caPool.Store(ncp)
  311. ifce.disconnectInvalid.Store(true)
  312. // Create manager
  313. conf := config.NewC(test.NewLogger())
  314. punchy := NewPunchyFromConfig(test.NewLogger(), conf)
  315. nc := newConnectionManagerFromConfig(test.NewLogger(), conf, hostMap, punchy)
  316. nc.intf = ifce
  317. ifce.connectionManager = nc
  318. hostinfo := &HostInfo{
  319. vpnAddrs: []netip.Addr{vpnIp},
  320. ConnectionState: &ConnectionState{
  321. myCert: &dummyCert{},
  322. peerCert: cachedPeerCert,
  323. },
  324. }
  325. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  326. // Move ahead 45s.
  327. // Check if to disconnect with invalid certificate.
  328. // Should be alive.
  329. nextTick := now.Add(45 * time.Second)
  330. invalid := nc.isInvalidCertificate(nextTick, hostinfo)
  331. assert.False(t, invalid)
  332. // Move ahead 61s.
  333. // Check if to disconnect with invalid certificate.
  334. // Should be disconnected.
  335. nextTick = now.Add(61 * time.Second)
  336. invalid = nc.isInvalidCertificate(nextTick, hostinfo)
  337. assert.True(t, invalid)
  338. }
  339. type dummyCert struct {
  340. version cert.Version
  341. curve cert.Curve
  342. groups []string
  343. isCa bool
  344. issuer string
  345. name string
  346. networks []netip.Prefix
  347. notAfter time.Time
  348. notBefore time.Time
  349. publicKey []byte
  350. signature []byte
  351. unsafeNetworks []netip.Prefix
  352. }
  353. func (d *dummyCert) Version() cert.Version {
  354. return d.version
  355. }
  356. func (d *dummyCert) Curve() cert.Curve {
  357. return d.curve
  358. }
  359. func (d *dummyCert) Groups() []string {
  360. return d.groups
  361. }
  362. func (d *dummyCert) IsCA() bool {
  363. return d.isCa
  364. }
  365. func (d *dummyCert) Issuer() string {
  366. return d.issuer
  367. }
  368. func (d *dummyCert) Name() string {
  369. return d.name
  370. }
  371. func (d *dummyCert) Networks() []netip.Prefix {
  372. return d.networks
  373. }
  374. func (d *dummyCert) NotAfter() time.Time {
  375. return d.notAfter
  376. }
  377. func (d *dummyCert) NotBefore() time.Time {
  378. return d.notBefore
  379. }
  380. func (d *dummyCert) PublicKey() []byte {
  381. return d.publicKey
  382. }
  383. func (d *dummyCert) MarshalPublicKeyPEM() []byte {
  384. return cert.MarshalPublicKeyToPEM(d.curve, d.publicKey)
  385. }
  386. func (d *dummyCert) Signature() []byte {
  387. return d.signature
  388. }
  389. func (d *dummyCert) UnsafeNetworks() []netip.Prefix {
  390. return d.unsafeNetworks
  391. }
  392. func (d *dummyCert) MarshalForHandshakes() ([]byte, error) {
  393. return nil, nil
  394. }
  395. func (d *dummyCert) Sign(curve cert.Curve, key []byte) error {
  396. return nil
  397. }
  398. func (d *dummyCert) CheckSignature(key []byte) bool {
  399. return true
  400. }
  401. func (d *dummyCert) Expired(t time.Time) bool {
  402. return false
  403. }
  404. func (d *dummyCert) CheckRootConstraints(signer cert.Certificate) error {
  405. return nil
  406. }
  407. func (d *dummyCert) VerifyPrivateKey(curve cert.Curve, key []byte) error {
  408. return nil
  409. }
  410. func (d *dummyCert) String() string {
  411. return ""
  412. }
  413. func (d *dummyCert) Marshal() ([]byte, error) {
  414. return nil, nil
  415. }
  416. func (d *dummyCert) MarshalPEM() ([]byte, error) {
  417. return nil, nil
  418. }
  419. func (d *dummyCert) Fingerprint() (string, error) {
  420. return "", nil
  421. }
  422. func (d *dummyCert) MarshalJSON() ([]byte, error) {
  423. return nil, nil
  424. }
  425. func (d *dummyCert) Copy() cert.Certificate {
  426. return d
  427. }