connection_manager_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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. ifce.pmtudManager = newPMTUDManagerFromConfig(test.NewLogger(), conf, ifce.inside)
  61. ifce.pmtudManager.intf = ifce
  62. p := []byte("")
  63. nb := make([]byte, 12, 12)
  64. out := make([]byte, mtu)
  65. // Add an ip we have established a connection w/ to hostmap
  66. hostinfo := &HostInfo{
  67. vpnAddrs: []netip.Addr{vpnIp},
  68. localIndexId: 1099,
  69. remoteIndexId: 9901,
  70. }
  71. hostinfo.ConnectionState = &ConnectionState{
  72. myCert: &dummyCert{version: cert.Version1},
  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. v1Credential: nil,
  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. ifce.pmtudManager = newPMTUDManagerFromConfig(test.NewLogger(), conf, ifce.inside)
  135. ifce.pmtudManager.intf = ifce
  136. p := []byte("")
  137. nb := make([]byte, 12, 12)
  138. out := make([]byte, mtu)
  139. // Add an ip we have established a connection w/ to hostmap
  140. hostinfo := &HostInfo{
  141. vpnAddrs: []netip.Addr{vpnIp},
  142. localIndexId: 1099,
  143. remoteIndexId: 9901,
  144. }
  145. hostinfo.ConnectionState = &ConnectionState{
  146. myCert: &dummyCert{version: cert.Version1},
  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.vpnAddrs[0])
  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.vpnAddrs[0])
  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.vpnAddrs[0])
  178. }
  179. func Test_NewConnectionManager_DisconnectInactive(t *testing.T) {
  180. l := test.NewLogger()
  181. localrange := netip.MustParsePrefix("10.1.1.1/24")
  182. vpnAddrs := []netip.Addr{netip.MustParseAddr("172.1.1.2")}
  183. preferredRanges := []netip.Prefix{localrange}
  184. // Very incomplete mock objects
  185. hostMap := newHostMap(l)
  186. hostMap.preferredRanges.Store(&preferredRanges)
  187. cs := &CertState{
  188. initiatingVersion: cert.Version1,
  189. privateKey: []byte{},
  190. v1Cert: &dummyCert{version: cert.Version1},
  191. v1Credential: nil,
  192. }
  193. lh := newTestLighthouse()
  194. ifce := &Interface{
  195. hostMap: hostMap,
  196. inside: &overlaytest.NoopTun{},
  197. outside: &udp.NoopConn{},
  198. firewall: &Firewall{},
  199. lightHouse: lh,
  200. pki: &PKI{},
  201. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  202. l: l,
  203. }
  204. ifce.pki.cs.Store(cs)
  205. // Create manager
  206. conf := config.NewC(test.NewLogger())
  207. conf.Settings["tunnels"] = map[string]any{
  208. "drop_inactive": true,
  209. }
  210. punchy := NewPunchyFromConfig(test.NewLogger(), conf)
  211. nc := newConnectionManagerFromConfig(test.NewLogger(), conf, hostMap, punchy)
  212. assert.True(t, nc.dropInactive.Load())
  213. nc.intf = ifce
  214. // Add an ip we have established a connection w/ to hostmap
  215. hostinfo := &HostInfo{
  216. vpnAddrs: vpnAddrs,
  217. localIndexId: 1099,
  218. remoteIndexId: 9901,
  219. }
  220. hostinfo.ConnectionState = &ConnectionState{
  221. myCert: &dummyCert{version: cert.Version1},
  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. v1Credential: nil,
  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.pmtudManager = newPMTUDManagerFromConfig(test.NewLogger(), conf, ifce.inside)
  324. ifce.pmtudManager.intf = ifce
  325. ifce.connectionManager = nc
  326. hostinfo := &HostInfo{
  327. vpnAddrs: []netip.Addr{vpnIp},
  328. ConnectionState: &ConnectionState{
  329. myCert: &dummyCert{},
  330. peerCert: cachedPeerCert,
  331. },
  332. }
  333. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  334. // Move ahead 45s.
  335. // Check if to disconnect with invalid certificate.
  336. // Should be alive.
  337. nextTick := now.Add(45 * time.Second)
  338. invalid := nc.isInvalidCertificate(nextTick, hostinfo)
  339. assert.False(t, invalid)
  340. // Move ahead 61s.
  341. // Check if to disconnect with invalid certificate.
  342. // Should be disconnected.
  343. nextTick = now.Add(61 * time.Second)
  344. invalid = nc.isInvalidCertificate(nextTick, hostinfo)
  345. assert.True(t, invalid)
  346. }
  347. type dummyCert struct {
  348. version cert.Version
  349. curve cert.Curve
  350. groups []string
  351. isCa bool
  352. issuer string
  353. name string
  354. networks []netip.Prefix
  355. notAfter time.Time
  356. notBefore time.Time
  357. publicKey []byte
  358. signature []byte
  359. unsafeNetworks []netip.Prefix
  360. }
  361. func (d *dummyCert) Version() cert.Version {
  362. return d.version
  363. }
  364. func (d *dummyCert) Curve() cert.Curve {
  365. return d.curve
  366. }
  367. func (d *dummyCert) Groups() []string {
  368. return d.groups
  369. }
  370. func (d *dummyCert) IsCA() bool {
  371. return d.isCa
  372. }
  373. func (d *dummyCert) Issuer() string {
  374. return d.issuer
  375. }
  376. func (d *dummyCert) Name() string {
  377. return d.name
  378. }
  379. func (d *dummyCert) Networks() []netip.Prefix {
  380. return d.networks
  381. }
  382. func (d *dummyCert) NotAfter() time.Time {
  383. return d.notAfter
  384. }
  385. func (d *dummyCert) NotBefore() time.Time {
  386. return d.notBefore
  387. }
  388. func (d *dummyCert) PublicKey() []byte {
  389. return d.publicKey
  390. }
  391. func (d *dummyCert) MarshalPublicKeyPEM() []byte {
  392. return cert.MarshalPublicKeyToPEM(d.curve, d.publicKey)
  393. }
  394. func (d *dummyCert) Signature() []byte {
  395. return d.signature
  396. }
  397. func (d *dummyCert) UnsafeNetworks() []netip.Prefix {
  398. return d.unsafeNetworks
  399. }
  400. func (d *dummyCert) MarshalForHandshakes() ([]byte, error) {
  401. return nil, nil
  402. }
  403. func (d *dummyCert) Sign(curve cert.Curve, key []byte) error {
  404. return nil
  405. }
  406. func (d *dummyCert) CheckSignature(key []byte) bool {
  407. return true
  408. }
  409. func (d *dummyCert) Expired(t time.Time) bool {
  410. return false
  411. }
  412. func (d *dummyCert) CheckRootConstraints(signer cert.Certificate) error {
  413. return nil
  414. }
  415. func (d *dummyCert) VerifyPrivateKey(curve cert.Curve, key []byte) error {
  416. return nil
  417. }
  418. func (d *dummyCert) String() string {
  419. return ""
  420. }
  421. func (d *dummyCert) Marshal() ([]byte, error) {
  422. return nil, nil
  423. }
  424. func (d *dummyCert) MarshalPEM() ([]byte, error) {
  425. return nil, nil
  426. }
  427. func (d *dummyCert) Fingerprint() (string, error) {
  428. return "", nil
  429. }
  430. func (d *dummyCert) MarshalJSON() ([]byte, error) {
  431. return nil, nil
  432. }
  433. func (d *dummyCert) Copy() cert.Certificate {
  434. return d
  435. }