pki_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package nebula
  2. import (
  3. "fmt"
  4. "net/netip"
  5. "strings"
  6. "testing"
  7. "time"
  8. "github.com/slackhq/nebula/cert"
  9. "github.com/slackhq/nebula/cert_test"
  10. "github.com/slackhq/nebula/config"
  11. "github.com/slackhq/nebula/test"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. )
  15. func TestNewCipherSuite(t *testing.T) {
  16. tests := []struct {
  17. name string
  18. curve cert.Curve
  19. cipher string
  20. fips140Enforced bool
  21. wantErr string
  22. // wantName is the full expected CipherSuite name (<DH>_<Cipher>_<Hash>),
  23. // only checked when wantErr is empty. Asserting the whole name makes both
  24. // the curve and cipher selection load-bearing.
  25. wantName string
  26. }{
  27. {
  28. name: "curve25519 aesgcm, not enforced",
  29. curve: cert.Curve_CURVE25519,
  30. cipher: "aesgcm",
  31. wantName: "25519_AESGCM_SHA256",
  32. },
  33. {
  34. name: "curve25519 chachapoly, not enforced",
  35. curve: cert.Curve_CURVE25519,
  36. cipher: "chachapoly",
  37. wantName: "25519_ChaChaPoly_SHA256",
  38. },
  39. {
  40. name: "p256 aesgcm, not enforced",
  41. curve: cert.Curve_P256,
  42. cipher: "aesgcm",
  43. wantName: "P256_AESGCM_SHA256",
  44. },
  45. {
  46. name: "p256 aesgcm, enforced is allowed",
  47. curve: cert.Curve_P256,
  48. cipher: "aesgcm",
  49. fips140Enforced: true,
  50. wantName: "P256_AESGCM_SHA256",
  51. },
  52. {
  53. name: "curve25519 rejected when enforced",
  54. curve: cert.Curve_CURVE25519,
  55. cipher: "aesgcm",
  56. fips140Enforced: true,
  57. wantErr: "pki: use of Curve25519 is not allowed in FIPS 140-only mode",
  58. },
  59. {
  60. name: "chachapoly rejected when enforced",
  61. curve: cert.Curve_P256,
  62. cipher: "chachapoly",
  63. fips140Enforced: true,
  64. wantErr: "pki: use of ChaChaPoly is not allowed in FIPS 140-only mode",
  65. },
  66. {
  67. // Curve is checked before cipher, so a Curve25519+ChaChaPoly
  68. // request reports the Curve25519 rejection.
  69. name: "curve25519 chachapoly rejected on curve when enforced",
  70. curve: cert.Curve_CURVE25519,
  71. cipher: "chachapoly",
  72. fips140Enforced: true,
  73. wantErr: "pki: use of Curve25519 is not allowed in FIPS 140-only mode",
  74. },
  75. }
  76. for _, tt := range tests {
  77. t.Run(tt.name, func(t *testing.T) {
  78. cs, err := newCipherSuite(tt.curve, false, tt.cipher, tt.fips140Enforced)
  79. if tt.wantErr != "" {
  80. require.EqualError(t, err, tt.wantErr)
  81. assert.Nil(t, cs)
  82. return
  83. }
  84. require.NoError(t, err)
  85. require.NotNil(t, cs)
  86. assert.Equal(t, tt.wantName, string(cs.Name()))
  87. })
  88. }
  89. }
  90. func TestNewCipherSuiteUnsupportedCurve(t *testing.T) {
  91. cs, err := newCipherSuite(cert.Curve(99), false, "aesgcm", false)
  92. require.Error(t, err)
  93. assert.True(t, strings.HasPrefix(err.Error(), "unsupported curve:"), "got: %v", err)
  94. assert.Nil(t, cs)
  95. }
  96. func TestPKIReloadRestartRequired(t *testing.T) {
  97. ca, _, caKey, caPEM := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Time{}, time.Time{}, nil, nil, nil)
  98. pkiConfig := func(cipher, network string) string {
  99. _, _, keyPEM, certPEM := cert_test.NewTestCert(cert.Version2, cert.Curve_CURVE25519, ca, caKey, "host", time.Time{}, time.Time{}, []netip.Prefix{netip.MustParsePrefix(network)}, nil, nil)
  100. return fmt.Sprintf("cipher: %s\npki:\n ca: %q\n cert: %q\n key: %q\n", cipher, caPEM, certPEM, keyPEM)
  101. }
  102. c := config.NewC(test.NewLogger())
  103. require.NoError(t, c.LoadString(pkiConfig("aes", "10.1.0.2/24")))
  104. p, err := NewPKIFromConfig(test.NewLogger(), c)
  105. require.NoError(t, err)
  106. // A cert that changes our networks is refused and needs a restart, it is not a failure
  107. res := c.ReloadConfigString(pkiConfig("aes", "10.1.0.3/24"))
  108. require.NoError(t, res.Err)
  109. assert.True(t, res.RestartRequired)
  110. assert.False(t, res.Failed)
  111. assert.Equal(t, []netip.Prefix{netip.MustParsePrefix("10.1.0.2/24")}, p.getCertState().myVpnNetworks)
  112. // The cipher only applies at startup
  113. c2 := config.NewC(test.NewLogger())
  114. require.NoError(t, c2.LoadString(pkiConfig("aes", "10.1.0.2/24")))
  115. p, err = NewPKIFromConfig(test.NewLogger(), c2)
  116. require.NoError(t, err)
  117. res = c2.ReloadConfigString(pkiConfig("chachapoly", "10.1.0.2/24"))
  118. require.NoError(t, res.Err)
  119. assert.True(t, res.RestartRequired)
  120. assert.Equal(t, "aes", p.getCertState().cipher)
  121. // A cert that keeps our networks applies without a restart
  122. c3 := config.NewC(test.NewLogger())
  123. require.NoError(t, c3.LoadString(pkiConfig("aes", "10.1.0.2/24")))
  124. _, err = NewPKIFromConfig(test.NewLogger(), c3)
  125. require.NoError(t, err)
  126. res = c3.ReloadConfigString(pkiConfig("aes", "10.1.0.2/24"))
  127. require.NoError(t, res.Err)
  128. assert.False(t, res.RestartRequired)
  129. // A cert that can't be read is a failure, and a restart wouldn't help
  130. res = c3.ReloadConfigString(strings.Replace(pkiConfig("aes", "10.1.0.2/24"), "cert: \"", "cert: \"garbage", 1))
  131. require.NoError(t, res.Err)
  132. assert.True(t, res.Failed)
  133. assert.False(t, res.RestartRequired)
  134. }
  135. // A cert for a version we weren't running is accepted without comparing networks, so switching to one with different
  136. // networks needs a restart. One with the same networks doesn't
  137. func TestPKIReloadNewVersionWithOtherNetworks(t *testing.T) {
  138. ca1, _, ca1Key, ca1PEM := cert_test.NewTestCaCert(cert.Version1, cert.Curve_CURVE25519, time.Time{}, time.Time{}, nil, nil, nil)
  139. ca2, _, ca2Key, ca2PEM := cert_test.NewTestCaCert(cert.Version2, cert.Curve_CURVE25519, time.Time{}, time.Time{}, nil, nil, nil)
  140. caPEM := string(ca1PEM) + string(ca2PEM)
  141. pkiConfig := func(v cert.Version, network string) string {
  142. ca, caKey := ca1, ca1Key
  143. if v == cert.Version2 {
  144. ca, caKey = ca2, ca2Key
  145. }
  146. _, _, keyPEM, certPEM := cert_test.NewTestCert(v, cert.Curve_CURVE25519, ca, caKey, "host", time.Time{}, time.Time{}, []netip.Prefix{netip.MustParsePrefix(network)}, nil, nil)
  147. return fmt.Sprintf("pki:\n ca: %q\n cert: %q\n key: %q\n", caPEM, certPEM, keyPEM)
  148. }
  149. for _, tc := range []struct {
  150. network string
  151. restart bool
  152. }{
  153. {"10.9.0.7/24", true},
  154. {"10.1.0.2/24", false},
  155. } {
  156. c := config.NewC(test.NewLogger())
  157. require.NoError(t, c.LoadString(pkiConfig(cert.Version1, "10.1.0.2/24")))
  158. p, err := NewPKIFromConfig(test.NewLogger(), c)
  159. require.NoError(t, err)
  160. res := c.ReloadConfigString(pkiConfig(cert.Version2, tc.network))
  161. require.NoError(t, res.Err)
  162. assert.False(t, res.Failed, tc.network)
  163. assert.Equal(t, tc.restart, res.RestartRequired, tc.network)
  164. assert.Equal(t, []netip.Prefix{netip.MustParsePrefix(tc.network)}, p.getCertState().myVpnNetworks, "accepted either way, as before")
  165. }
  166. }