connection_manager_test.go 14 KB

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