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