From 26b72e20d3ce29f982ce84f328be1c983e94bd22 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 1 Mar 2023 17:16:03 +0000 Subject: [PATCH] fix: hardcode azure instance identity intermediate certificates This was broken for an airgapped customer! --- coderd/azureidentity/azureidentity.go | 93 +++++++++++++++++----- coderd/azureidentity/azureidentity_test.go | 3 +- coderd/workspaceresourceauth.go | 2 +- 3 files changed, 75 insertions(+), 23 deletions(-) diff --git a/coderd/azureidentity/azureidentity.go b/coderd/azureidentity/azureidentity.go index b1161edfc8278..436a9c952a180 100644 --- a/coderd/azureidentity/azureidentity.go +++ b/coderd/azureidentity/azureidentity.go @@ -1,12 +1,10 @@ package azureidentity import ( - "context" "crypto/x509" "encoding/base64" "encoding/json" - "io" - "net/http" + "encoding/pem" "regexp" "go.mozilla.org/pkcs7" @@ -23,7 +21,7 @@ type metadata struct { // Validate ensures the signature was signed by an Azure certificate. // It returns the associated VM ID if successful. -func Validate(ctx context.Context, signature string, options x509.VerifyOptions) (string, error) { +func Validate(signature string, options x509.VerifyOptions) (string, error) { data, err := base64.StdEncoding.DecodeString(signature) if err != nil { return "", xerrors.Errorf("decode base64: %w", err) @@ -41,24 +39,14 @@ func Validate(ctx context.Context, signature string, options x509.VerifyOptions) } if options.Intermediates == nil { options.Intermediates = x509.NewCertPool() - for _, certURL := range signer.IssuingCertificateURL { - req, err := http.NewRequestWithContext(ctx, "GET", certURL, nil) - if err != nil { - return "", xerrors.Errorf("new request %q: %w", certURL, err) - } - res, err := http.DefaultClient.Do(req) - if err != nil { - return "", xerrors.Errorf("perform request %q: %w", certURL, err) + for _, cert := range certificates { + block, rest := pem.Decode([]byte(cert)) + if len(rest) != 0 { + return "", xerrors.Errorf("invalid certificate. %d bytes remain", len(rest)) } - data, err := io.ReadAll(res.Body) + cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - _ = res.Body.Close() - return "", xerrors.Errorf("read body %q: %w", certURL, err) - } - _ = res.Body.Close() - cert, err := x509.ParseCertificate(data) - if err != nil { - return "", xerrors.Errorf("parse certificate %q: %w", certURL, err) + return "", xerrors.Errorf("parse certificate: %w", err) } options.Intermediates.AddCert(cert) } @@ -76,3 +64,68 @@ func Validate(ctx context.Context, signature string, options x509.VerifyOptions) } return metadata.VMID, nil } + +var certificates = []string{ + `-----BEGIN CERTIFICATE----- +MIIFWjCCBEKgAwIBAgIQDxSWXyAgaZlP1ceseIlB4jANBgkqhkiG9w0BAQsFADBa +MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl +clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw +MDcyMTIzMDAwMFoXDTI0MTAwODA3MDAwMFowTzELMAkGA1UEBhMCVVMxHjAcBgNV +BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEgMB4GA1UEAxMXTWljcm9zb2Z0IFJT +QSBUTFMgQ0EgMDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqYnfP +mmOyBoTzkDb0mfMUUavqlQo7Rgb9EUEf/lsGWMk4bgj8T0RIzTqk970eouKVuL5R +IMW/snBjXXgMQ8ApzWRJCZbar879BV8rKpHoAW4uGJssnNABf2n17j9TiFy6BWy+ +IhVnFILyLNK+W2M3zK9gheiWa2uACKhuvgCca5Vw/OQYErEdG7LBEzFnMzTmJcli +W1iCdXby/vI/OxbfqkKD4zJtm45DJvC9Dh+hpzqvLMiK5uo/+aXSJY+SqhoIEpz+ +rErHw+uAlKuHFtEjSeeku8eR3+Z5ND9BSqc6JtLqb0bjOHPm5dSRrgt4nnil75bj +c9j3lWXpBb9PXP9Sp/nPCK+nTQmZwHGjUnqlO9ebAVQD47ZisFonnDAmjrZNVqEX +F3p7laEHrFMxttYuD81BdOzxAbL9Rb/8MeFGQjE2Qx65qgVfhH+RsYuuD9dUw/3w +ZAhq05yO6nk07AM9c+AbNtRoEcdZcLCHfMDcbkXKNs5DJncCqXAN6LhXVERCw/us +G2MmCMLSIx9/kwt8bwhUmitOXc6fpT7SmFvRAtvxg84wUkg4Y/Gx++0j0z6StSeN +0EJz150jaHG6WV4HUqaWTb98Tm90IgXAU4AW2GBOlzFPiU5IY9jt+eXC2Q6yC/Zp +TL1LAcnL3Qa/OgLrHN0wiw1KFGD51WRPQ0Sh7QIDAQABo4IBJTCCASEwHQYDVR0O +BBYEFLV2DDARzseSQk1Mx1wsyKkM6AtkMB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoI +VDaGezq1BE3wMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI +KwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0BggrBgEFBQcBAQQoMCYwJAYI +KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTA6BgNVHR8EMzAxMC+g +LaArhilodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vT21uaXJvb3QyMDI1LmNybDAq +BgNVHSAEIzAhMAgGBmeBDAECATAIBgZngQwBAgIwCwYJKwYBBAGCNyoBMA0GCSqG +SIb3DQEBCwUAA4IBAQCfK76SZ1vae4qt6P+dTQUO7bYNFUHR5hXcA2D59CJWnEj5 +na7aKzyowKvQupW4yMH9fGNxtsh6iJswRqOOfZYC4/giBO/gNsBvwr8uDW7t1nYo +DYGHPpvnpxCM2mYfQFHq576/TmeYu1RZY29C4w8xYBlkAA8mDJfRhMCmehk7cN5F +JtyWRj2cZj/hOoI45TYDBChXpOlLZKIYiG1giY16vhCRi6zmPzEwv+tk156N6cGS +Vm44jTQ/rs1sa0JSYjzUaYngoFdZC4OfxnIkQvUIA4TOFmPzNPEFdjcZsgbeEz4T +cGHTBPK4R28F44qIMCtHRV55VMX53ev6P3hRddJb +-----END CERTIFICATE-----`, + `-----BEGIN CERTIFICATE----- +MIIFWjCCBEKgAwIBAgIQD6dHIsU9iMgPWJ77H51KOjANBgkqhkiG9w0BAQsFADBa +MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl +clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw +MDcyMTIzMDAwMFoXDTI0MTAwODA3MDAwMFowTzELMAkGA1UEBhMCVVMxHjAcBgNV +BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEgMB4GA1UEAxMXTWljcm9zb2Z0IFJT +QSBUTFMgQ0EgMDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD0wBlZ +qiokfAYhMdHuEvWBapTj9tFKL+NdsS4pFDi8zJVdKQfR+F039CDXtD9YOnqS7o88 ++isKcgOeQNTri472mPnn8N3vPCX0bDOEVk+nkZNIBA3zApvGGg/40Thv78kAlxib +MipsKahdbuoHByOB4ZlYotcBhf/ObUf65kCRfXMRQqOKWkZLkilPPn3zkYM5GHxe +I4MNZ1SoKBEoHa2E/uDwBQVxadY4SRZWFxMd7ARyI4Cz1ik4N2Z6ALD3MfjAgEED +woknyw9TGvr4PubAZdqU511zNLBoavar2OAVTl0Tddj+RAhbnX1/zypqk+ifv+d3 +CgiDa8Mbvo1u2Q8nuUBrKVUmR6EjkV/dDrIsUaU643v/Wp/uE7xLDdhC5rplK9si +NlYohMTMKLAkjxVeWBWbQj7REickISpc+yowi3yUrO5lCgNAKrCNYw+wAfAvhFkO +eqPm6kP41IHVXVtGNC/UogcdiKUiR/N59IfYB+o2v54GMW+ubSC3BohLFbho/oZZ +5XyulIZK75pwTHmauCIeE5clU9ivpLwPTx9b0Vno9+ApElrFgdY0/YKZ46GfjOC9 +ta4G25VJ1WKsMmWLtzyrfgwbYopquZd724fFdpvsxfIvMG5m3VFkThOqzsOttDcU +fyMTqM2pan4txG58uxNJ0MjR03UCEULRU+qMnwIDAQABo4IBJTCCASEwHQYDVR0O +BBYEFP8vf+EG9DjzLe0ljZjC/g72bPz6MB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoI +VDaGezq1BE3wMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYI +KwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADA0BggrBgEFBQcBAQQoMCYwJAYI +KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTA6BgNVHR8EMzAxMC+g +LaArhilodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vT21uaXJvb3QyMDI1LmNybDAq +BgNVHSAEIzAhMAgGBmeBDAECATAIBgZngQwBAgIwCwYJKwYBBAGCNyoBMA0GCSqG +SIb3DQEBCwUAA4IBAQCg2d165dQ1tHS0IN83uOi4S5heLhsx+zXIOwtxnvwCWdOJ +3wFLQaFDcgaMtN79UjMIFVIUedDZBsvalKnx+6l2tM/VH4YAyNPx+u1LFR0joPYp +QYLbNYkedkNuhRmEBesPqj4aDz68ZDI6fJ92sj2q18QvJUJ5Qz728AvtFOat+Ajg +K0PFqPYEAviUKr162NB1XZJxf6uyIjUlnG4UEdHfUqdhl0R84mMtrYINksTzQ2sH +YM8fEhqICtTlcRLr/FErUaPUe9648nziSnA0qKH7rUZqP/Ifmbo+WNZSZG1BbgOh +lk+521W+Ncih3HRbvRBE0LWYT8vWKnfjgZKxwHwJ +-----END CERTIFICATE-----`, +} diff --git a/coderd/azureidentity/azureidentity_test.go b/coderd/azureidentity/azureidentity_test.go index ce02e9dab80b6..ed0646b106563 100644 --- a/coderd/azureidentity/azureidentity_test.go +++ b/coderd/azureidentity/azureidentity_test.go @@ -1,7 +1,6 @@ package azureidentity_test import ( - "context" "crypto/x509" "testing" "time" @@ -19,7 +18,7 @@ func TestValidate(t *testing.T) { t.Parallel() ct, err := time.Parse(time.RFC3339, "2023-02-01T00:00:00Z") require.NoError(t, err) - vm, err := azureidentity.Validate(context.Background(), signature, x509.VerifyOptions{ + vm, err := azureidentity.Validate(signature, x509.VerifyOptions{ CurrentTime: ct, }) require.NoError(t, err) diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index dfafe7ba1e853..115b39591622e 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -37,7 +37,7 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r if !httpapi.Read(ctx, rw, r, &req) { return } - instanceID, err := azureidentity.Validate(ctx, req.Signature, api.AzureCertificates) + instanceID, err := azureidentity.Validate(req.Signature, api.AzureCertificates) if err != nil { httpapi.Write(ctx, rw, http.StatusUnauthorized, codersdk.Response{ Message: "Invalid Azure identity.",