2
0

connection_manager_test.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. package nebula
  2. import (
  3. "context"
  4. "crypto/ed25519"
  5. "crypto/rand"
  6. "net"
  7. "testing"
  8. "time"
  9. "github.com/flynn/noise"
  10. "github.com/slackhq/nebula/cert"
  11. "github.com/slackhq/nebula/config"
  12. "github.com/slackhq/nebula/iputil"
  13. "github.com/slackhq/nebula/test"
  14. "github.com/slackhq/nebula/udp"
  15. "github.com/stretchr/testify/assert"
  16. )
  17. var vpnIp iputil.VpnIp
  18. func newTestLighthouse() *LightHouse {
  19. lh := &LightHouse{
  20. l: test.NewLogger(),
  21. addrMap: map[iputil.VpnIp]*RemoteList{},
  22. queryChan: make(chan iputil.VpnIp, 10),
  23. }
  24. lighthouses := map[iputil.VpnIp]struct{}{}
  25. staticList := map[iputil.VpnIp]struct{}{}
  26. lh.lighthouses.Store(&lighthouses)
  27. lh.staticList.Store(&staticList)
  28. return lh
  29. }
  30. func Test_NewConnectionManagerTest(t *testing.T) {
  31. l := test.NewLogger()
  32. //_, tuncidr, _ := net.ParseCIDR("1.1.1.1/24")
  33. _, vpncidr, _ := net.ParseCIDR("172.1.1.1/24")
  34. _, localrange, _ := net.ParseCIDR("10.1.1.1/24")
  35. vpnIp = iputil.Ip2VpnIp(net.ParseIP("172.1.1.2"))
  36. preferredRanges := []*net.IPNet{localrange}
  37. // Very incomplete mock objects
  38. hostMap := newHostMap(l, vpncidr)
  39. hostMap.preferredRanges.Store(&preferredRanges)
  40. cs := &CertState{
  41. RawCertificate: []byte{},
  42. PrivateKey: []byte{},
  43. Certificate: &cert.NebulaCertificate{},
  44. RawCertificateNoKey: []byte{},
  45. }
  46. lh := newTestLighthouse()
  47. ifce := &Interface{
  48. hostMap: hostMap,
  49. inside: &test.NoopTun{},
  50. outside: &udp.NoopConn{},
  51. firewall: &Firewall{},
  52. lightHouse: lh,
  53. pki: &PKI{},
  54. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  55. l: l,
  56. }
  57. ifce.pki.cs.Store(cs)
  58. // Create manager
  59. ctx, cancel := context.WithCancel(context.Background())
  60. defer cancel()
  61. punchy := NewPunchyFromConfig(l, config.NewC(l))
  62. nc := newConnectionManager(ctx, l, ifce, 5, 10, punchy)
  63. p := []byte("")
  64. nb := make([]byte, 12, 12)
  65. out := make([]byte, mtu)
  66. // Add an ip we have established a connection w/ to hostmap
  67. hostinfo := &HostInfo{
  68. vpnIp: vpnIp,
  69. localIndexId: 1099,
  70. remoteIndexId: 9901,
  71. }
  72. hostinfo.ConnectionState = &ConnectionState{
  73. myCert: &cert.NebulaCertificate{},
  74. H: &noise.HandshakeState{},
  75. }
  76. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  77. // We saw traffic out to vpnIp
  78. nc.Out(hostinfo.localIndexId)
  79. nc.In(hostinfo.localIndexId)
  80. assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId)
  81. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  82. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  83. assert.Contains(t, nc.out, hostinfo.localIndexId)
  84. // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded
  85. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  86. assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId)
  87. assert.NotContains(t, nc.out, hostinfo.localIndexId)
  88. assert.NotContains(t, nc.in, hostinfo.localIndexId)
  89. // Do another traffic check tick, this host should be pending deletion now
  90. nc.Out(hostinfo.localIndexId)
  91. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  92. assert.Contains(t, nc.pendingDeletion, hostinfo.localIndexId)
  93. assert.NotContains(t, nc.out, hostinfo.localIndexId)
  94. assert.NotContains(t, nc.in, hostinfo.localIndexId)
  95. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  96. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  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.pendingDeletion, hostinfo.localIndexId)
  100. assert.NotContains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  101. assert.NotContains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  102. }
  103. func Test_NewConnectionManagerTest2(t *testing.T) {
  104. l := test.NewLogger()
  105. //_, tuncidr, _ := net.ParseCIDR("1.1.1.1/24")
  106. _, vpncidr, _ := net.ParseCIDR("172.1.1.1/24")
  107. _, localrange, _ := net.ParseCIDR("10.1.1.1/24")
  108. preferredRanges := []*net.IPNet{localrange}
  109. // Very incomplete mock objects
  110. hostMap := newHostMap(l, vpncidr)
  111. hostMap.preferredRanges.Store(&preferredRanges)
  112. cs := &CertState{
  113. RawCertificate: []byte{},
  114. PrivateKey: []byte{},
  115. Certificate: &cert.NebulaCertificate{},
  116. RawCertificateNoKey: []byte{},
  117. }
  118. lh := newTestLighthouse()
  119. ifce := &Interface{
  120. hostMap: hostMap,
  121. inside: &test.NoopTun{},
  122. outside: &udp.NoopConn{},
  123. firewall: &Firewall{},
  124. lightHouse: lh,
  125. pki: &PKI{},
  126. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  127. l: l,
  128. }
  129. ifce.pki.cs.Store(cs)
  130. // Create manager
  131. ctx, cancel := context.WithCancel(context.Background())
  132. defer cancel()
  133. punchy := NewPunchyFromConfig(l, config.NewC(l))
  134. nc := newConnectionManager(ctx, l, ifce, 5, 10, punchy)
  135. p := []byte("")
  136. nb := make([]byte, 12, 12)
  137. out := make([]byte, mtu)
  138. // Add an ip we have established a connection w/ to hostmap
  139. hostinfo := &HostInfo{
  140. vpnIp: vpnIp,
  141. localIndexId: 1099,
  142. remoteIndexId: 9901,
  143. }
  144. hostinfo.ConnectionState = &ConnectionState{
  145. myCert: &cert.NebulaCertificate{},
  146. H: &noise.HandshakeState{},
  147. }
  148. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  149. // We saw traffic out to vpnIp
  150. nc.Out(hostinfo.localIndexId)
  151. nc.In(hostinfo.localIndexId)
  152. assert.NotContains(t, nc.pendingDeletion, hostinfo.vpnIp)
  153. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  154. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  155. // Do a traffic check tick, should not be pending deletion but should not have any in/out packets recorded
  156. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  157. assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId)
  158. assert.NotContains(t, nc.out, hostinfo.localIndexId)
  159. assert.NotContains(t, nc.in, hostinfo.localIndexId)
  160. // Do another traffic check tick, this host should be pending deletion now
  161. nc.Out(hostinfo.localIndexId)
  162. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  163. assert.Contains(t, nc.pendingDeletion, hostinfo.localIndexId)
  164. assert.NotContains(t, nc.out, hostinfo.localIndexId)
  165. assert.NotContains(t, nc.in, hostinfo.localIndexId)
  166. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  167. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  168. // We saw traffic, should no longer be pending deletion
  169. nc.In(hostinfo.localIndexId)
  170. nc.doTrafficCheck(hostinfo.localIndexId, p, nb, out, time.Now())
  171. assert.NotContains(t, nc.pendingDeletion, hostinfo.localIndexId)
  172. assert.NotContains(t, nc.out, hostinfo.localIndexId)
  173. assert.NotContains(t, nc.in, hostinfo.localIndexId)
  174. assert.Contains(t, nc.hostMap.Indexes, hostinfo.localIndexId)
  175. assert.Contains(t, nc.hostMap.Hosts, hostinfo.vpnIp)
  176. }
  177. // Check if we can disconnect the peer.
  178. // Validate if the peer's certificate is invalid (expired, etc.)
  179. // Disconnect only if disconnectInvalid: true is set.
  180. func Test_NewConnectionManagerTest_DisconnectInvalid(t *testing.T) {
  181. now := time.Now()
  182. l := test.NewLogger()
  183. ipNet := net.IPNet{
  184. IP: net.IPv4(172, 1, 1, 2),
  185. Mask: net.IPMask{255, 255, 255, 0},
  186. }
  187. _, vpncidr, _ := net.ParseCIDR("172.1.1.1/24")
  188. _, localrange, _ := net.ParseCIDR("10.1.1.1/24")
  189. preferredRanges := []*net.IPNet{localrange}
  190. hostMap := newHostMap(l, vpncidr)
  191. hostMap.preferredRanges.Store(&preferredRanges)
  192. // Generate keys for CA and peer's cert.
  193. pubCA, privCA, _ := ed25519.GenerateKey(rand.Reader)
  194. caCert := cert.NebulaCertificate{
  195. Details: cert.NebulaCertificateDetails{
  196. Name: "ca",
  197. NotBefore: now,
  198. NotAfter: now.Add(1 * time.Hour),
  199. IsCA: true,
  200. PublicKey: pubCA,
  201. },
  202. }
  203. assert.NoError(t, caCert.Sign(cert.Curve_CURVE25519, privCA))
  204. ncp := &cert.NebulaCAPool{
  205. CAs: cert.NewCAPool().CAs,
  206. }
  207. ncp.CAs["ca"] = &caCert
  208. pubCrt, _, _ := ed25519.GenerateKey(rand.Reader)
  209. peerCert := cert.NebulaCertificate{
  210. Details: cert.NebulaCertificateDetails{
  211. Name: "host",
  212. Ips: []*net.IPNet{&ipNet},
  213. Subnets: []*net.IPNet{},
  214. NotBefore: now,
  215. NotAfter: now.Add(60 * time.Second),
  216. PublicKey: pubCrt,
  217. IsCA: false,
  218. Issuer: "ca",
  219. },
  220. }
  221. assert.NoError(t, peerCert.Sign(cert.Curve_CURVE25519, privCA))
  222. cs := &CertState{
  223. RawCertificate: []byte{},
  224. PrivateKey: []byte{},
  225. Certificate: &cert.NebulaCertificate{},
  226. RawCertificateNoKey: []byte{},
  227. }
  228. lh := newTestLighthouse()
  229. ifce := &Interface{
  230. hostMap: hostMap,
  231. inside: &test.NoopTun{},
  232. outside: &udp.NoopConn{},
  233. firewall: &Firewall{},
  234. lightHouse: lh,
  235. handshakeManager: NewHandshakeManager(l, hostMap, lh, &udp.NoopConn{}, defaultHandshakeConfig),
  236. l: l,
  237. pki: &PKI{},
  238. }
  239. ifce.pki.cs.Store(cs)
  240. ifce.pki.caPool.Store(ncp)
  241. ifce.disconnectInvalid.Store(true)
  242. // Create manager
  243. ctx, cancel := context.WithCancel(context.Background())
  244. defer cancel()
  245. punchy := NewPunchyFromConfig(l, config.NewC(l))
  246. nc := newConnectionManager(ctx, l, ifce, 5, 10, punchy)
  247. ifce.connectionManager = nc
  248. hostinfo := &HostInfo{
  249. vpnIp: vpnIp,
  250. ConnectionState: &ConnectionState{
  251. myCert: &cert.NebulaCertificate{},
  252. peerCert: &peerCert,
  253. H: &noise.HandshakeState{},
  254. },
  255. }
  256. nc.hostMap.unlockedAddHostInfo(hostinfo, ifce)
  257. // Move ahead 45s.
  258. // Check if to disconnect with invalid certificate.
  259. // Should be alive.
  260. nextTick := now.Add(45 * time.Second)
  261. invalid := nc.isInvalidCertificate(nextTick, hostinfo)
  262. assert.False(t, invalid)
  263. // Move ahead 61s.
  264. // Check if to disconnect with invalid certificate.
  265. // Should be disconnected.
  266. nextTick = now.Add(61 * time.Second)
  267. invalid = nc.isInvalidCertificate(nextTick, hostinfo)
  268. assert.True(t, invalid)
  269. }