connection_manager_test.go 15 KB

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