diff --git a/.github/workflows/typos.toml b/.github/workflows/typos.toml index fd962da6dc..6cc06617da 100644 --- a/.github/workflows/typos.toml +++ b/.github/workflows/typos.toml @@ -54,6 +54,8 @@ extend-exclude = [ "tailnet/testdata/**", "site/src/pages/SetupPage/countries.tsx", "provisioner/terraform/testdata/**", + "coderd/azureidentity/roots_darwin.go", + "coderd/azureidentity/azureidentity.go", # notifications' golden files confuse the detector because of quoted-printable encoding "coderd/notifications/testdata/**", "agent/agentcontainers/testdata/devcontainercli/**", diff --git a/coderd/azureidentity/azureidentity.go b/coderd/azureidentity/azureidentity.go index cf7756826e..eb451a0a53 100644 --- a/coderd/azureidentity/azureidentity.go +++ b/coderd/azureidentity/azureidentity.go @@ -184,7 +184,9 @@ type metadata struct { type Options struct { // Roots is the trusted root certificate pool. If nil, - // the embedded root certificate pool is used. + // the default cert pool is used. On darwin, this is an + // embedded pool. On all other platforms it is the system + // pool. Roots *x509.CertPool // Intermediates are additional intermediate certificates to // inject into the PKCS7 object for chain verification. Azure @@ -335,104 +337,6 @@ func ParseCertificates() ([]*x509.Certificate, error) { return certs, nil } -// Roots are the root CAs that Azure instance-identity certificates chain to. -// These are embedded so verification works deterministically on all -// platforms, including macOS where the system verifier would otherwise be -// used and may reject otherwise valid Azure certificates due to stricter -// standards-compliance checks. See https://github.com/coder/coder/issues/12978. -var Roots = []string{ - // DigiCert Global Root G2 - `-----BEGIN CERTIFICATE----- -MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH -MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI -2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx -1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ -q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz -tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ -vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV -5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY -1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 -NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG -Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 -8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe -pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl -MrY= ------END CERTIFICATE-----`, - // DigiCert Global Root G3 - `-----BEGIN CERTIFICATE----- -MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe -Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw -EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x -IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG -fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO -Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd -BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx -AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ -oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 -sycX ------END CERTIFICATE-----`, - // Baltimore CyberTrust Root. - // Required for chains rooted here, e.g. "Microsoft RSA TLS CA 01/02". - // Expired 2025-05-12 but kept so callers that pass a CurrentTime - // before the expiry can still verify historical signatures. - `-----BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE-----`, -} - -// rootCertPool returns a CertPool containing the root CAs that Azure -// instance-identity certificates ultimately chain to. We embed these so -// callers do not have to populate Roots themselves, and so we never -// implicitly fall back to the platform's system verifier (notably Apple's -// Security framework on macOS/iOS) which enforces stricter standards- -// compliance checks than Go's pure-Go verifier and rejects some otherwise -// valid Azure leaf certificates. -var rootCertPool = sync.OnceValues(func() (*x509.CertPool, error) { - pool := x509.NewCertPool() - for _, pemCert := range Roots { - block, rest := pem.Decode([]byte(pemCert)) - if block == nil { - return nil, xerrors.New("root: failed to decode PEM block") - } - if len(rest) != 0 { - return nil, xerrors.Errorf("root: invalid certificate, %d bytes remain", len(rest)) - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, xerrors.Errorf("root: parse certificate: %w", err) - } - pool.AddCert(cert) - } - return pool, nil -}) - // Certificates are manually downloaded from Azure, then processed with OpenSSL // and added here. See: https://learn.microsoft.com/en-us/azure/security/fundamentals/azure-ca-details // diff --git a/coderd/azureidentity/azureidentity_test.go b/coderd/azureidentity/azureidentity_test.go index f5138e64ac..04e2f003cd 100644 --- a/coderd/azureidentity/azureidentity_test.go +++ b/coderd/azureidentity/azureidentity_test.go @@ -8,7 +8,6 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/base64" - "encoding/pem" "math/big" "runtime" "testing" @@ -62,40 +61,6 @@ func TestValidate(t *testing.T) { } } -// TestEmbeddedRoots ensures the package's embedded root certificates parse -// successfully. The Roots are used by Validate to avoid falling back to the -// platform's system verifier (notably Apple's Security framework on macOS), -// which previously caused TestValidate/regular to fail on macOS with -// `x509: "metadata.azure.com" certificate is not standards compliant`. -// See https://github.com/coder/coder/issues/12978. -func TestEmbeddedRoots(t *testing.T) { - t.Parallel() - require.NotEmpty(t, azureidentity.Roots, "embedded roots must not be empty") - seen := map[string]bool{} - for _, pemCert := range azureidentity.Roots { - block, rest := pem.Decode([]byte(pemCert)) - require.NotNil(t, block, "PEM block should decode") - require.Zero(t, len(rest), "no trailing data after PEM block") - cert, err := x509.ParseCertificate(block.Bytes) - require.NoError(t, err) - // Each root must be self-signed (issuer == subject). - require.Equal(t, cert.Issuer.String(), cert.Subject.String(), - "root certificate must be self-signed: %s", cert.Subject.CommonName) - require.False(t, seen[cert.Subject.CommonName], - "duplicate embedded root: %s", cert.Subject.CommonName) - seen[cert.Subject.CommonName] = true - } - // Verify the three roots Azure instance-identity chains ultimately - // terminate at are all present. - for _, name := range []string{ - "DigiCert Global Root G2", - "DigiCert Global Root G3", - "Baltimore CyberTrust Root", - } { - require.True(t, seen[name], "missing embedded root %q", name) - } -} - func TestExpiresSoon(t *testing.T) { t.Parallel() // TODO (@kylecarbs): It's unknown why Microsoft does not have new certificates live... @@ -261,9 +226,6 @@ func (tc *testCertChain) validationOptions() azureidentity.Options { func TestValidate_TamperedContent(t *testing.T) { t.Parallel() - if runtime.GOOS == "darwin" { - t.Skip("pkcs7 signing uses SHA1 which may be restricted on macOS") - } chain := newTestCertChain(t) diff --git a/coderd/azureidentity/roots_darwin.go b/coderd/azureidentity/roots_darwin.go new file mode 100644 index 0000000000..edf6bfcfb7 --- /dev/null +++ b/coderd/azureidentity/roots_darwin.go @@ -0,0 +1,111 @@ +//go:build darwin + +package azureidentity + +import ( + "crypto/x509" + "encoding/pem" + "sync" + + "golang.org/x/xerrors" +) + +// rootCertPool returns a CertPool containing the root CAs that Azure +// instance-identity certificates ultimately chain to. On macOS, we embed these +// because Apple's Security framework enforces stricter standards-compliance +// checks than Go's pure-Go verifier and rejects some otherwise valid Azure leaf +// certificates. However, we want to avoid hardcoding the roots on other +// platforms because if Azure changes their root CAs, we want operators to be +// able to validate without having to get a new Coder binary. macOS support for +// coderd is only intended for development and testing, so this is a small trade +// off. +var rootCertPool = sync.OnceValues(func() (*x509.CertPool, error) { + pool := x509.NewCertPool() + for _, pemCert := range embeddedRoots { + block, rest := pem.Decode([]byte(pemCert)) + if block == nil { + return nil, xerrors.New("root: failed to decode PEM block") + } + if len(rest) != 0 { + return nil, xerrors.Errorf("root: invalid certificate, %d bytes remain", len(rest)) + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, xerrors.Errorf("root: parse certificate: %w", err) + } + pool.AddCert(cert) + } + return pool, nil +}) + +// embeddedRoots are the root CAs that Azure instance-identity certificates +// chain to. These are embedded so verification works on macOS where the system +// verifier would otherwise be used and may reject otherwise valid Azure +// certificates due to stricter standards-compliance checks. +// See https://github.com/coder/coder/issues/12978. +var embeddedRoots = []string{ + // DigiCert Global Root G2 + `-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE-----`, + // DigiCert Global Root G3 + `-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE-----`, + // Baltimore CyberTrust Root. + // Required for chains rooted here, e.g. "Microsoft RSA TLS CA 01/02". + // Expired 2025-05-12 but kept so callers that pass a CurrentTime + // before the expiry can still verify historical signatures. + `-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE-----`, +} diff --git a/coderd/azureidentity/roots_darwin_internal_test.go b/coderd/azureidentity/roots_darwin_internal_test.go new file mode 100644 index 0000000000..461c43465b --- /dev/null +++ b/coderd/azureidentity/roots_darwin_internal_test.go @@ -0,0 +1,45 @@ +//go:build darwin + +package azureidentity + +import ( + "crypto/x509" + "encoding/pem" + "testing" + + "github.com/stretchr/testify/require" +) + +// TestEmbeddedRoots ensures the package's embedded root certificates parse +// successfully. The roots are used by Validate to avoid falling back to the +// platform's system verifier (notably Apple's Security framework on macOS), +// which previously caused TestValidate/regular to fail on macOS with +// `x509: "metadata.azure.com" certificate is not standards compliant`. +// See https://github.com/coder/coder/issues/12978. +func TestEmbeddedRoots(t *testing.T) { + t.Parallel() + require.NotEmpty(t, embeddedRoots, "embedded roots must not be empty") + seen := map[string]bool{} + for _, pemCert := range embeddedRoots { + block, rest := pem.Decode([]byte(pemCert)) + require.NotNil(t, block, "PEM block should decode") + require.Zero(t, len(rest), "no trailing data after PEM block") + cert, err := x509.ParseCertificate(block.Bytes) + require.NoError(t, err) + // Each root must be self-signed (issuer == subject). + require.Equal(t, cert.Issuer.String(), cert.Subject.String(), + "root certificate must be self-signed: %s", cert.Subject.CommonName) + require.False(t, seen[cert.Subject.CommonName], + "duplicate embedded root: %s", cert.Subject.CommonName) + seen[cert.Subject.CommonName] = true + } + // Verify the three roots Azure instance-identity chains ultimately + // terminate at are all present. + for _, name := range []string{ + "DigiCert Global Root G2", + "DigiCert Global Root G3", + "Baltimore CyberTrust Root", + } { + require.True(t, seen[name], "missing embedded root %q", name) + } +} diff --git a/coderd/azureidentity/roots_other.go b/coderd/azureidentity/roots_other.go new file mode 100644 index 0000000000..d12731f2d8 --- /dev/null +++ b/coderd/azureidentity/roots_other.go @@ -0,0 +1,10 @@ +//go:build !darwin + +package azureidentity + +import "crypto/x509" + +// rootCertPool returns the system cert pool on non-Apple platforms. +func rootCertPool() (*x509.CertPool, error) { + return x509.SystemCertPool() +}