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  }