github.com/argoproj/argo-cd@v1.8.7/util/tls/tls_test.go (about) 1 package tls 2 3 import ( 4 "crypto/tls" 5 "crypto/x509" 6 "encoding/pem" 7 "fmt" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/assert" 13 ) 14 15 var chain = `-----BEGIN CERTIFICATE----- 16 MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs 17 MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 18 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j 19 ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL 20 MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 21 LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug 22 RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ 23 PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC 24 7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw 25 PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 26 4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo 27 LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U 28 pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy 29 BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH 30 AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH 31 AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o 32 dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 33 AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 34 AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp 35 AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl 36 AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo 37 AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg 38 AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg 39 AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB 40 gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy 41 dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy 42 dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw 43 gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB 44 c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 45 LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE 46 FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI 47 Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU 48 nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/ 49 roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU 50 xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+ 51 BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu 52 zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA 53 -----END CERTIFICATE----- 54 -----BEGIN CERTIFICATE----- 55 MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs 56 MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 57 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j 58 ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL 59 MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 60 LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug 61 RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm 62 +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW 63 PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM 64 xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB 65 Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 66 hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg 67 EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF 68 MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA 69 FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec 70 nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z 71 eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF 72 hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 73 Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe 74 vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep 75 +OkuE6N36B9K 76 -----END CERTIFICATE-----` 77 78 var privateKey = `-----BEGIN RSA PRIVATE KEY----- 79 MIIBsAIBAAKBgQCgF35rHhOWi9+r4n9xM/ejvMEsQ8h6lams962k4U0WSdfySUev 80 hyI1bd3FRIb5fFqSBt6qPTiiiIw0KXte5dANB6lPe6HdUPTA/U4xHWi2FB/BfAyP 81 sOlUBfFp6dtkEEcEKt+Z8KTJYJEerRie24y+nsfZMnLBst6tsEBfx/U75wIBAwKB 82 gGq6VEdpYmRdHGzsbmP7vDiYe2zYHLwQ0AKnPKNErq6KQyQC5eEngbgT4WpWl+J2 83 Xn+R9m0vwNbaiDam0uD3p5192BaN2tdaW5P5JjfGa95ytRBCQ/cr+z03FjG9C6zQ 84 QZG5eyOoMloHAfnYiJMV5SZarfTiF9BGFvtcfrjhbterAgkDBMoUFjHxL0ECeDUI 85 f9nbOl1O2AgI/51gfHGo/NKv+kcQenM8RO7dy9+hUAulwqMlyszSq+0GdZdgQL/i 86 Lz8NclSgyuUtptmaSWtjB5Tdc8boaBApGKac7vB4M1AfTkng1+SplKbkdFlCVg4n 87 6EvCOrUFFsLp308JSbkv2240Q93JJwIJAgMxYrl2oMorAgcDNY7r7ttvAggOb9tA 88 6WMDHQ== 89 -----END RSA PRIVATE KEY-----` 90 91 func decodePem(certInput string) tls.Certificate { 92 var cert tls.Certificate 93 certPEMBlock := []byte(certInput) 94 var certDERBlock *pem.Block 95 for { 96 certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) 97 if certDERBlock == nil { 98 break 99 } 100 if certDERBlock.Type == "CERTIFICATE" { 101 cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) 102 } 103 } 104 105 var keyDERBlock *pem.Block 106 keyPEMBlock := []byte(privateKey) 107 keyDERBlock, _ = pem.Decode(keyPEMBlock) 108 cert.PrivateKey, _ = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) 109 return cert 110 } 111 112 func TestEncodeX509KeyPairString(t *testing.T) { 113 certChain := decodePem(chain) 114 cert, _ := EncodeX509KeyPairString(certChain) 115 116 if strings.TrimSpace(chain) != strings.TrimSpace(cert) { 117 t.Errorf("Incorrect, got: %s, want: %s", cert, chain) 118 } 119 120 } 121 122 func TestGetTLSVersionByString(t *testing.T) { 123 t.Run("Valid versions", func(t *testing.T) { 124 for k, v := range tlsVersionByString { 125 r, err := getTLSVersionByString(k) 126 assert.NoError(t, err) 127 assert.Equal(t, v, r) 128 } 129 }) 130 131 t.Run("Invalid versions", func(t *testing.T) { 132 _, err := getTLSVersionByString("1.4") 133 assert.Error(t, err) 134 }) 135 136 t.Run("Empty versions", func(t *testing.T) { 137 r, err := getTLSVersionByString("") 138 assert.NoError(t, err) 139 assert.Equal(t, r, uint16(0)) 140 }) 141 } 142 143 func TestGetTLSCipherSuitesByString(t *testing.T) { 144 suites := make([]string, 0) 145 for _, s := range tls.CipherSuites() { 146 t.Run(fmt.Sprintf("Test for valid suite %s", s.Name), func(t *testing.T) { 147 ids, err := getTLSCipherSuitesByString(s.Name) 148 assert.NoError(t, err) 149 assert.Len(t, ids, 1) 150 assert.Equal(t, s.ID, ids[0]) 151 suites = append(suites, s.Name) 152 }) 153 } 154 155 t.Run("Test colon separated list", func(t *testing.T) { 156 ids, err := getTLSCipherSuitesByString(strings.Join(suites, ":")) 157 assert.NoError(t, err) 158 assert.Len(t, ids, len(suites)) 159 }) 160 161 suites = append([]string{"invalid"}, suites...) 162 t.Run("Test invalid values", func(t *testing.T) { 163 _, err := getTLSCipherSuitesByString(strings.Join(suites, ":")) 164 assert.Error(t, err) 165 }) 166 167 } 168 169 func TestTLSVersionToString(t *testing.T) { 170 t.Run("Test known versions", func(t *testing.T) { 171 versions := make([]uint16, 0) 172 for _, v := range tlsVersionByString { 173 versions = append(versions, v) 174 } 175 s := tlsVersionsToStr(versions) 176 assert.Len(t, s, len(versions)) 177 }) 178 t.Run("Test unknown version", func(t *testing.T) { 179 s := tlsVersionsToStr([]uint16{999}) 180 assert.Len(t, s, 1) 181 assert.Equal(t, "unknown", s[0]) 182 }) 183 } 184 185 func TestGenerate(t *testing.T) { 186 t.Run("Invalid: No hosts specified", func(t *testing.T) { 187 opts := CertOptions{Hosts: []string{}, Organization: "Acme", ValidFrom: time.Now(), ValidFor: 10 * time.Hour} 188 _, _, err := generate(opts) 189 assert.Error(t, err) 190 assert.Contains(t, err.Error(), "hosts not supplied") 191 }) 192 193 t.Run("Invalid: No organization specified", func(t *testing.T) { 194 opts := CertOptions{Hosts: []string{"localhost"}, Organization: "", ValidFrom: time.Now(), ValidFor: 10 * time.Hour} 195 _, _, err := generate(opts) 196 assert.Error(t, err) 197 assert.Contains(t, err.Error(), "organization not supplied") 198 }) 199 200 t.Run("Invalid: Unsupported curve specified", func(t *testing.T) { 201 opts := CertOptions{Hosts: []string{"localhost"}, Organization: "Acme", ECDSACurve: "Curve?", ValidFrom: time.Now(), ValidFor: 10 * time.Hour} 202 _, _, err := generate(opts) 203 assert.Error(t, err) 204 assert.Contains(t, err.Error(), "Unrecognized elliptic curve") 205 }) 206 207 for _, curve := range []string{"P224", "P256", "P384", "P521"} { 208 t.Run(fmt.Sprintf("Create certificate with curve %s", curve), func(t *testing.T) { 209 opts := CertOptions{Hosts: []string{"localhost"}, Organization: "Acme", ECDSACurve: curve} 210 _, _, err := generate(opts) 211 assert.NoError(t, err) 212 }) 213 } 214 215 t.Run("Create certificate with default options", func(t *testing.T) { 216 opts := CertOptions{Hosts: []string{"localhost"}, Organization: "Acme"} 217 certBytes, privKey, err := generate(opts) 218 assert.NoError(t, err) 219 assert.NotNil(t, privKey) 220 cert, err := x509.ParseCertificate(certBytes) 221 assert.NoError(t, err) 222 assert.NotNil(t, cert) 223 assert.Len(t, cert.DNSNames, 1) 224 assert.Equal(t, "localhost", cert.DNSNames[0]) 225 assert.Empty(t, cert.IPAddresses) 226 assert.LessOrEqual(t, int64(time.Since(cert.NotBefore)), int64(10*time.Second)) 227 }) 228 229 t.Run("Create certificate with IP ", func(t *testing.T) { 230 opts := CertOptions{Hosts: []string{"localhost", "127.0.0.1"}, Organization: "Acme"} 231 certBytes, privKey, err := generate(opts) 232 assert.NoError(t, err) 233 assert.NotNil(t, privKey) 234 cert, err := x509.ParseCertificate(certBytes) 235 assert.NoError(t, err) 236 assert.NotNil(t, cert) 237 assert.Len(t, cert.DNSNames, 1) 238 assert.Equal(t, "localhost", cert.DNSNames[0]) 239 assert.Equal(t, "Acme", cert.Subject.Organization[0]) 240 assert.Len(t, cert.IPAddresses, 1) 241 assert.Equal(t, "127.0.0.1", cert.IPAddresses[0].String()) 242 }) 243 244 t.Run("Create certificate with specific validity timeframe", func(t *testing.T) { 245 opts := CertOptions{Hosts: []string{"localhost"}, Organization: "Acme", ValidFrom: time.Now().Add(1 * time.Hour)} 246 certBytes, privKey, err := generate(opts) 247 assert.NoError(t, err) 248 assert.NotNil(t, privKey) 249 cert, err := x509.ParseCertificate(certBytes) 250 assert.NoError(t, err) 251 assert.NotNil(t, cert) 252 assert.GreaterOrEqual(t, (time.Now().Unix())+int64(1*time.Hour), cert.NotBefore.Unix()) 253 }) 254 } 255 256 func TestGeneratePEM(t *testing.T) { 257 t.Run("Invalid - PEM creation failure", func(t *testing.T) { 258 opts := CertOptions{Hosts: nil, Organization: "Acme"} 259 cert, key, err := generatePEM(opts) 260 assert.Error(t, err) 261 assert.Nil(t, cert) 262 assert.Nil(t, key) 263 }) 264 265 t.Run("Create PEM from certficate options", func(t *testing.T) { 266 opts := CertOptions{Hosts: []string{"localhost"}, Organization: "Acme"} 267 cert, key, err := generatePEM(opts) 268 assert.NoError(t, err) 269 assert.NotNil(t, cert) 270 assert.NotNil(t, key) 271 }) 272 273 t.Run("Create X509KeyPair", func(t *testing.T) { 274 opts := CertOptions{Hosts: []string{"localhost"}, Organization: "Acme"} 275 cert, err := GenerateX509KeyPair(opts) 276 assert.NoError(t, err) 277 assert.NotNil(t, cert) 278 }) 279 } 280 281 func TestGetTLSConfigCustomizer(t *testing.T) { 282 t.Run("Valid TLS customization", func(t *testing.T) { 283 cfunc, err := getTLSConfigCustomizer(DefaultTLSMinVersion, DefaultTLSMaxVersion, DefaultTLSCipherSuite) 284 assert.NoError(t, err) 285 assert.NotNil(t, cfunc) 286 config := tls.Config{} 287 cfunc(&config) 288 assert.Equal(t, config.MinVersion, uint16(tls.VersionTLS12)) 289 assert.Equal(t, config.MaxVersion, uint16(tls.VersionTLS13)) 290 }) 291 292 t.Run("Valid TLS customization - No cipher customization for TLSv1.3 only with default ciphers", func(t *testing.T) { 293 cfunc, err := getTLSConfigCustomizer("1.3", "1.3", DefaultTLSCipherSuite) 294 assert.NoError(t, err) 295 assert.NotNil(t, cfunc) 296 config := tls.Config{} 297 cfunc(&config) 298 assert.Equal(t, config.MinVersion, uint16(tls.VersionTLS13)) 299 assert.Equal(t, config.MaxVersion, uint16(tls.VersionTLS13)) 300 assert.Len(t, config.CipherSuites, 0) 301 }) 302 303 t.Run("Valid TLS customization - No cipher customization for TLSv1.3 only with custom ciphers", func(t *testing.T) { 304 cfunc, err := getTLSConfigCustomizer("1.3", "1.3", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") 305 assert.NoError(t, err) 306 assert.NotNil(t, cfunc) 307 config := tls.Config{} 308 cfunc(&config) 309 assert.Equal(t, config.MinVersion, uint16(tls.VersionTLS13)) 310 assert.Equal(t, config.MaxVersion, uint16(tls.VersionTLS13)) 311 assert.Len(t, config.CipherSuites, 0) 312 }) 313 314 t.Run("Invalid TLS customization - Min version higher than max version", func(t *testing.T) { 315 cfunc, err := getTLSConfigCustomizer("1.3", "1.2", DefaultTLSCipherSuite) 316 assert.Error(t, err) 317 assert.Nil(t, cfunc) 318 }) 319 320 t.Run("Invalid TLS customization - Invalid min version given", func(t *testing.T) { 321 cfunc, err := getTLSConfigCustomizer("2.0", "1.2", DefaultTLSCipherSuite) 322 assert.Error(t, err) 323 assert.Nil(t, cfunc) 324 }) 325 326 t.Run("Invalid TLS customization - Invalid max version given", func(t *testing.T) { 327 cfunc, err := getTLSConfigCustomizer("1.2", "2.0", DefaultTLSCipherSuite) 328 assert.Error(t, err) 329 assert.Nil(t, cfunc) 330 }) 331 332 t.Run("Invalid TLS customization - Unknown cipher suite given", func(t *testing.T) { 333 cfunc, err := getTLSConfigCustomizer("1.3", "1.2", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:invalid") 334 assert.Error(t, err) 335 assert.Nil(t, cfunc) 336 }) 337 338 } 339 340 func TestBestEffortSystemCertPool(t *testing.T) { 341 pool := BestEffortSystemCertPool() 342 assert.NotNil(t, pool) 343 }