From 62aeb4da967d54a2fd7005332fb96d24ba1d7739 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:27:10 -0800 Subject: [PATCH] policy: add client cert SAN match criteria Expand the Certificate Matcher to support matching on DNS, email, or URI Subject Alternative Names, using the existing String Matcher conditions. --- pkg/policy/criteria/client_certificate.go | 6 ++ .../criteria/client_certificate_test.go | 79 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/pkg/policy/criteria/client_certificate.go b/pkg/policy/criteria/client_certificate.go index 7082b2e80..c9805169a 100644 --- a/pkg/policy/criteria/client_certificate.go +++ b/pkg/policy/criteria/client_certificate.go @@ -50,6 +50,12 @@ func (c clientCertificateCriterion) GenerateRule( err = addCertFingerprintCondition(&body, v) case "spki_hash": err = addCertSPKIHashCondition(&body, v) + case "san_email": + err = matchString(&body, ast.VarTerm("cert.EmailAddresses[_]"), v) + case "san_dns": + err = matchString(&body, ast.VarTerm("cert.DNSNames[_]"), v) + case "san_uri": + err = matchString(&body, ast.VarTerm("cert.URIStrings[_]"), v) default: err = fmt.Errorf("unsupported certificate matcher condition: %s", k) } diff --git a/pkg/policy/criteria/client_certificate_test.go b/pkg/policy/criteria/client_certificate_test.go index 20164eef3..aedaef236 100644 --- a/pkg/policy/criteria/client_certificate_test.go +++ b/pkg/policy/criteria/client_certificate_test.go @@ -23,6 +23,25 @@ RAIgProROtxpvKS/qjrjonSvacnhdU0JwoXj2DgYvF/qjrUCIAXlHkdEzyXmTLuu /YxuOibV35vlaIzj21GRj4pYmVR1 -----END CERTIFICATE-----` +// testCertWithSANs is a certificate with 6 Subject Alternative Names: +// DNS:1.example.com, DNS:2.example.com, email:email-1@example.com, +// email:email-2@example.com, URI:https://example.com/uri-1, and +// URI:https://example.com/uri-2. +const testCertWithSANs = ` +-----BEGIN CERTIFICATE----- +MIIB9TCCAZugAwIBAgIDAIABMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0 +ZWQgUm9vdCBDQTAeFw0yNDAxMjIyMzU1NTNaFw0zNDAxMTkyMzU1NTNaMCUxIzAh +BgNVBAMTGmNsaWVudCBjZXJ0IHdpdGggbWFueSBTQU5zMFkwEwYHKoZIzj0CAQYI +KoZIzj0DAQcDQgAEJNVizgh7I/609xD6Dik7QrIzwSp6zIgSeKEfekic7r3rd8fC +0W84UORBjFXxRa4nxj8tyanN4PreD1veACPjHqOBxDCBwTATBgNVHSUEDDAKBggr +BgEFBQcDAjAfBgNVHSMEGDAWgBQ1yn6j/DJdpmqyNIV8/lJBYsIuyzCBiAYDVR0R +BIGAMH6CDTEuZXhhbXBsZS5jb22CDTIuZXhhbXBsZS5jb22BE2VtYWlsLTFAZXhh +bXBsZS5jb22BE2VtYWlsLTJAZXhhbXBsZS5jb22GGWh0dHBzOi8vZXhhbXBsZS5j +b20vdXJpLTGGGWh0dHBzOi8vZXhhbXBsZS5jb20vdXJpLTIwCgYIKoZIzj0EAwID +SAAwRQIgKqRs9N3EOmzW2ZPQgJh2un6XaQbXtyE9O9TZEQGFr2gCIQCC16tr754m +z60udX689FtwwnWYmteZsZstBoEbPSTzWw== +-----END CERTIFICATE-----` + func TestClientCertificate(t *testing.T) { t.Parallel() @@ -90,6 +109,66 @@ func TestClientCertificate(t *testing.T) { testCert, A{true, A{ReasonClientCertificateOK}, M{}}, }, + { + "no email match", + `allow: + or: + - client_certificate: + san_email: + is: not-present@example.com`, + testCertWithSANs, + A{false, A{ReasonClientCertificateUnauthorized}, M{}}, + }, + { + "email match", + `allow: + or: + - client_certificate: + san_email: + is: email-1@example.com`, + testCertWithSANs, + A{true, A{ReasonClientCertificateOK}, M{}}, + }, + { + "no dns match", + `allow: + or: + - client_certificate: + san_dns: + is: not-present.example.com`, + testCertWithSANs, + A{false, A{ReasonClientCertificateUnauthorized}, M{}}, + }, + { + "dns match", + `allow: + or: + - client_certificate: + san_dns: + is: 1.example.com`, + testCertWithSANs, + A{true, A{ReasonClientCertificateOK}, M{}}, + }, + { + "no uri match", + `allow: + or: + - client_certificate: + san_uri: + is: https://example.com/not-present`, + testCertWithSANs, + A{false, A{ReasonClientCertificateUnauthorized}, M{}}, + }, + { + "uri match", + `allow: + or: + - client_certificate: + san_uri: + is: 'https://example.com/uri-1'`, + testCertWithSANs, + A{true, A{ReasonClientCertificateOK}, M{}}, + }, } for i := range cases {