From a6960ea173299dca607a114517a883c9124b437d Mon Sep 17 00:00:00 2001 From: Zac Bergquist Date: Wed, 27 Sep 2023 14:54:37 -0600 Subject: [PATCH] Fix self-signed cert validity on macOS systems As per https://support.apple.com/en-in/HT210176: > TLS server certificates must contain an ExtendedKeyUsage (EKU) extension containing the id-kp-serverAuth OID. We were not specifying this EKU. Validated by checking with the old self-signed certs: $ security verify-cert -c webproxy_cert.pem -p ssl -r webproxy_cert.pem Cert Verify Result: Invalid Extended Key Usage for policy And then repeating the process after this change: $ security verify-cert -c webproxy_cert.pem -p ssl -r webproxy_cert.pem ...certificate verification successful. Closes #32531 --- lib/teleterm/grpccredentials.go | 4 ++-- lib/teleterm/teleterm_test.go | 3 ++- lib/utils/cert/selfsigned.go | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/teleterm/grpccredentials.go b/lib/teleterm/grpccredentials.go index 634b2199152cd..378409f00f882 100644 --- a/lib/teleterm/grpccredentials.go +++ b/lib/teleterm/grpccredentials.go @@ -99,7 +99,7 @@ func createClientTLSConfig(clientKeyPair tls.Certificate, serverCertPath string) }, nil } -func generateAndSaveCert(targetPath string) (tls.Certificate, error) { +func generateAndSaveCert(targetPath string, eku ...x509.ExtKeyUsage) (tls.Certificate, error) { // The cert is first saved under a temp path and then renamed to targetPath. This prevents other // processes from reading a half-written file. tempFile, err := os.CreateTemp(filepath.Dir(targetPath), filepath.Base(targetPath)) @@ -108,7 +108,7 @@ func generateAndSaveCert(targetPath string) (tls.Certificate, error) { } defer os.Remove(tempFile.Name()) - cert, err := cert.GenerateSelfSignedCert([]string{"localhost"}, nil) + cert, err := cert.GenerateSelfSignedCert([]string{"localhost"}, nil, eku...) if err != nil { return tls.Certificate{}, trace.Wrap(err, "failed to generate the certificate") } diff --git a/lib/teleterm/teleterm_test.go b/lib/teleterm/teleterm_test.go index aa703ed5ea157..d677334a479b2 100644 --- a/lib/teleterm/teleterm_test.go +++ b/lib/teleterm/teleterm_test.go @@ -17,6 +17,7 @@ package teleterm import ( "context" "crypto/tls" + "crypto/x509" "errors" "fmt" "net" @@ -209,7 +210,7 @@ func createValidClientTLSConfig(t *testing.T, certsDir string) *tls.Config { // reach the tsh gRPC server, so we need to use the renderer cert as the client cert. clientCertPath := filepath.Join(certsDir, rendererCertFileName) serverCertPath := filepath.Join(certsDir, tshdCertFileName) - clientCert, err := generateAndSaveCert(clientCertPath) + clientCert, err := generateAndSaveCert(clientCertPath, x509.ExtKeyUsageClientAuth) require.NoError(t, err) tlsConfig, err := createClientTLSConfig(clientCert, serverCertPath) diff --git a/lib/utils/cert/selfsigned.go b/lib/utils/cert/selfsigned.go index 8957a018a3ae1..93b44acd6e42f 100644 --- a/lib/utils/cert/selfsigned.go +++ b/lib/utils/cert/selfsigned.go @@ -39,7 +39,7 @@ import ( // verifier and not in Go. const macMaxTLSCertValidityPeriod = 825 * 24 * time.Hour -// Credentials keeps the typical 3 components of a proper HTTPS configuration +// Credentials keeps the typical 3 components of a proper TLS configuration type Credentials struct { // PublicKey in PEM format PublicKey []byte @@ -49,8 +49,16 @@ type Credentials struct { } // GenerateSelfSignedCert generates a self-signed certificate that -// is valid for given domain names and ips, returns PEM-encoded bytes with key and cert -func GenerateSelfSignedCert(hostNames []string, ipAddresses []string) (*Credentials, error) { +// is valid for given domain names and IPs. If extended key usage +// is not specified, the cert will be generated for server auth. +func GenerateSelfSignedCert(hostNames []string, ipAddresses []string, eku ...x509.ExtKeyUsage) (*Credentials, error) { + if len(eku) == 0 { + // if not specified, assume this cert is for server auth, + // which is required for validation on macOS: + // https://support.apple.com/en-in/HT210176 + eku = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} + } + priv, err := native.GenerateRSAPrivateKey() if err != nil { return nil, trace.Wrap(err) @@ -77,6 +85,7 @@ func GenerateSelfSignedCert(hostNames []string, ipAddresses []string) (*Credenti NotBefore: notBefore, NotAfter: notAfter, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: eku, BasicConstraintsValid: true, IsCA: true, }