From dbedfc586fb8e5fed58057e39c74903604fe1107 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Fri, 26 Jul 2024 10:23:19 -0700 Subject: [PATCH] add mTLS UserPrincipalName SAN match (#5177) Add a new 'user_principal_name' type to the downstream mTLS match_subject_alt_names option. This corresponds to the 'OtherName' type with type-id 1.3.6.1.4.1.311.20.2.3 and a UTF8String value. Add support for UserPrincipalName SAN matching to the policy evaluator. --- authorize/evaluator/functions.go | 78 +++++ authorize/evaluator/functions_test.go | 321 ++++++++++++++---- authorize/evaluator/gen-test-certs.go | 43 +++ authorize/evaluator/headers_evaluator_test.go | 2 +- config/envoyconfig/listeners_test.go | 11 + config/mtls.go | 16 + config/mtls_test.go | 1 + go.mod | 8 +- go.sum | 20 +- 9 files changed, 416 insertions(+), 84 deletions(-) diff --git a/authorize/evaluator/functions.go b/authorize/evaluator/functions.go index 10ab2f863..e84f74728 100644 --- a/authorize/evaluator/functions.go +++ b/authorize/evaluator/functions.go @@ -3,14 +3,18 @@ package evaluator import ( "context" "crypto/x509" + "encoding/asn1" "encoding/json" "encoding/pem" "errors" "fmt" "regexp" + "slices" "strings" lru "github.com/hashicorp/golang-lru/v2" + "golang.org/x/crypto/cryptobyte" + cb_asn1 "golang.org/x/crypto/cryptobyte/asn1" "github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/internal/log" @@ -135,6 +139,16 @@ func verifyClientCertificate( crls map[string]*x509.RevocationList, constraints ClientCertConstraints, ) error { + // If a SubjectAltName extension is: + // - marked as critical, and + // - contains only name types that are not recognized by the Go standard + // library (i.e. no DNS, email address, IP address, or URI names) + // then the Go parsing code will add it to the UnhandleCriticalExtensions + // field of the Certificate struct. This will fail the Verify() call below. + // Because we support other SAN matching checks, let's avoid this behavior. + cert.UnhandledCriticalExtensions = slices.DeleteFunc(cert.UnhandledCriticalExtensions, + func(oid asn1.ObjectIdentifier) bool { return oid.Equal(oidSubjectAltName) }) + chains, err := cert.Verify(x509.VerifyOptions{ Roots: roots, Intermediates: intermediates, @@ -242,6 +256,17 @@ func validateClientCertificateSANs(chain []*x509.Certificate, matchers SANMatche } } } + if r := matchers[config.SANTypeUserPrincipalName]; r != nil { + names, err := getUserPrincipalNamesFromCert(cert) + if err != nil { + return err + } + for _, name := range names { + if r.MatchString(name) { + return nil + } + } + } return errNoSANMatch } @@ -256,3 +281,56 @@ func parseCertificate(pemStr string) (*x509.Certificate, error) { } return x509.ParseCertificate(block.Bytes) } + +var ( + oidSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17} + oidUserPrincipalName = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 20, 2, 3} + otherNameTag = cb_asn1.Tag(0).Constructed().ContextSpecific() + otherNameValueTag = cb_asn1.Tag(0).Constructed().ContextSpecific() +) + +func getUserPrincipalNamesFromSAN(raw []byte) ([]string, error) { + san := cryptobyte.String(raw) + var generalNames cryptobyte.String + if !san.ReadASN1(&generalNames, cb_asn1.SEQUENCE) { + return nil, errors.New("error reading GeneralNames sequence") + } + var upns []string + for !generalNames.Empty() { + var name cryptobyte.String + var tag cb_asn1.Tag + if !generalNames.ReadAnyASN1(&name, &tag) { + return nil, errors.New("error reading GeneralName") + } else if tag != otherNameTag { + continue + } + + var oid asn1.ObjectIdentifier + if !name.ReadASN1ObjectIdentifier(&oid) { + return nil, errors.New("error reading OtherName type ID") + } else if !oid.Equal(oidUserPrincipalName) { + continue + } + + var value cryptobyte.String + if !name.ReadASN1(&value, otherNameValueTag) { + return nil, errors.New("error reading UserPrincipalName value") + } + + var utf8string cryptobyte.String + if !value.ReadASN1(&utf8string, cb_asn1.UTF8String) { + return nil, errors.New("error reading UserPrincipalName: expected UTF8String") + } + upns = append(upns, string(utf8string)) + } + return upns, nil +} + +func getUserPrincipalNamesFromCert(cert *x509.Certificate) ([]string, error) { + for _, ext := range cert.Extensions { + if ext.Id.Equal(oidSubjectAltName) { + return getUserPrincipalNamesFromSAN(ext.Value) + } + } + return nil, nil +} diff --git a/authorize/evaluator/functions_test.go b/authorize/evaluator/functions_test.go index 05517c91b..c31d148c1 100644 --- a/authorize/evaluator/functions_test.go +++ b/authorize/evaluator/functions_test.go @@ -1,6 +1,7 @@ package evaluator import ( + "encoding/asn1" "regexp" "testing" @@ -19,133 +20,146 @@ const ( testCA = ` -----BEGIN CERTIFICATE----- MIIBaDCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAaMRgw +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAaMRgw FgYDVQQDEw9UcnVzdGVkIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AAR2/RkzmSK6paoeTKFx1Bd52ZCg29ulJlMxFdSZT8FlmmaK9mN6KWwO+NHYObiW -y3AQuoSTrZXlrlRW5ANvMI+io0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0E -AwIDSAAwRQIhAMU5/NjpitOSbUobtjeOriPH8JRo9qy1iFyeVNAcdVvgAiAewq2A -PhgzWTw5F9PJg++9i+xGQTqHs3ZirG27cCjvhQ== +AAS+oiuwekZ86TUjhJQV12ZjAlt+3Zy/VkRuj7tA7wtFwEs8w77iQryIQO/DEccY +9coUjLfWFc/V5LTsNlYTh4B8o0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwCgYIKoZIzj0E +AwIDSAAwRQIhAMH+UKlJxWDdtKH7YRnhSefk/AxfjdjrDJhQKm4EUrVjAiAxxvdP +yoEpPAq1NW/Ny9yuKE8mfRTWgu+09L3jOwDqTg== -----END CERTIFICATE----- ` testValidCert = ` -----BEGIN CERTIFICATE----- -MIIBYjCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAeMRww +MIIBYTCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAeMRww GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D -AQcDQgAEcWa1Bz6mpsLnM1VD8gtzELjzjEp9Dopp/xWScFO9qtay5SBOeX+Ftr0O -8+/RkoKHzGgZ80gr6xQyUJL3MCwVZKM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw -HwYDVR0jBBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0EAwIDSAAw -RQIgXM1ogmy0vcz4lYzji5X3In1n2GLOFNTgucFPkM0GtqgCIQCsXPs/0OjSFyDR -FBqAm1NqDJcxq685fS9t3VfHwapcVA== +AQcDQgAED3L4Hf7kBaa6E76kRaideTYPS+deD+T1+qwfD5amUF4h3dblJCQgDuWl +p9WA7PzJioroP4HeUUVll8GF4Ngx/aM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw +HwYDVR0jBBgwFoAU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwCgYIKoZIzj0EAwIDRwAw +RAIgeS3o3PnBZTGSM5yFuxl+xZQwItUGOvk4TUIHbLKIh+QCIGYABXVpXyaHT1/7 +xAdvB6E8eNv4/LIbPo3OKVwzLWka -----END CERTIFICATE----- ` testUntrustedCert = ` -----BEGIN CERTIFICATE----- -MIIBZjCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz -dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDcxODAzMjFaMCAx +MIIBZzCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz +dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zNDA3MjMxODA3NDlaMCAx HjAcBgNVBAMTFXVudHJ1c3RlZCBjbGllbnQgY2VydDBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABJxEIKqLhhMEm5XZXkT+p+hlC2TFyaW0HIZqoE9navJrAcUB8L2M -mVQ+/wLaCznJHLeSLn46uGH5p1hoGFqOrdajODA2MBMGA1UdJQQMMAoGCCsGAQUF -BwMCMB8GA1UdIwQYMBaAFIp2rlIiSnr33ea3cGyLsX4LEYwWMAoGCCqGSM49BAMC -A0gAMEUCIDtJIZJDcqIYaDXhZFs0nd0nHER8IGP9n4BBFMWewAb2AiEAlQyavOxw -iTQQxt0rXB4Ox5zWpU9q68+F9BGBkQKTsBs= +SM49AwEHA0IABD7wlyIZI0dk81W93CPi0C9EK5oWnP9jyf6ukNSfuN2/AifVyskZ +ZaJsC1y0x11eHQn0AKDvFZvrM5ntgtEKvTujODA2MBMGA1UdJQQMMAoGCCsGAQUF +BwMCMB8GA1UdIwQYMBaAFCWSSHqA46gKGwIZH3RAYyds6hJ0MAoGCCqGSM49BAMC +A0kAMEYCIQDeg+lSgmzg5W85HpMjnfcbDxOwTFAuG2dBZIFm4MeZRAIhAOhReIDb +B0ktSaPCNQOn1m0PMi4OxnSrClWV0pQzZwiM -----END CERTIFICATE----- ` testRevokedCert = ` -----BEGIN CERTIFICATE----- -MIIBYjCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAeMRww +MIIBYzCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAeMRww GgYDVQQDExNyZXZva2VkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D -AQcDQgAEcnoO4EM72C7xL31RE9e6m9YJYyF6E4JloASECd8mdiXPlMXIjq8MZHB5 -28mFAVQNE7erAtBftID1SbuY4IpXxqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw -HwYDVR0jBBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0EAwIDSAAw -RQIgUUETSO064YIu+VKnyRb0yBnNTjXLy3TvGuYgZI8VX0YCIQDd0gyNEC5YLvRN -njxfnLoimp+TzTVzvsCokUbNSNRKJA== +AQcDQgAE5sRZhAupCq3X0KWDNRfJh0H0jOGNOQawaKFejCQQ0t2kXCvfkvcTlGWo +Cjlgtc885wLI5n0KPG5ugN1Zk7nrlqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw +HwYDVR0jBBgwFoAU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwCgYIKoZIzj0EAwIDSQAw +RgIhAIzYBLOWFTLdWvf//svgjUAtjW51/qvuR+oUQSpyJEs7AiEAlmGwYPnUpVah +1ri42dcuvYe6u+E7yWQOvF9MRVBQFrI= -----END CERTIFICATE----- ` testCRL = ` -----BEGIN X509 CRL----- MIHeMIGFAgEBMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0ZWQgUm9vdCBD -QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTIzMDgxMDE4MDMyMVqgMDAuMB8G -A1UdIwQYMBaAFCRhVVNlDrzlYGmPWHAlIrpxqYgkMAsGA1UdFAQEAgIgADAKBggq -hkjOPQQDAgNIADBFAiEAumtTtjiQt1VsbsEnyr+xbpK0KmzKvkpxIVgE1M9CND0C -IA8zx5clcaGIT5xRnBLZW7RwA37IOmB+7zjAuJQpmKKp +QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTI0MDcyNTE4MDc0OVqgMDAuMB8G +A1UdIwQYMBaAFOIUzZsarER1GfDZP5tN0R6OhxsgMAsGA1UdFAQEAgIgADAKBggq +hkjOPQQDAgNIADBFAiAMtFj+hNOleIXozxi6QJJrRtS/7LyYrDuRju/CKvbxhwIh +AKXWklJEPCyzzhIt4ETiesXhMu8UUngRhuWwGLFQoyJH -----END X509 CRL----- ` testIntermediateCA = ` -----BEGIN CERTIFICATE----- MIIBkTCCATegAwIBAgICEAMwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAiMSAw +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAiMSAw HgYDVQQDExdUcnVzdGVkIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABMY+zxL/2dNORuha3uVVOXZYIkTpa9V8N9UVrM15HOHkrdLlz1qk -4wbePkkoGtNRzoayb0iZqeA4YjOxqyPG8emjYzBhMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQpGNmcLLM3vHiOADYGPDQL8AhkyDAf -BgNVHSMEGDAWgBQkYVVTZQ685WBpj1hwJSK6camIJDAKBggqhkjOPQQDAgNIADBF -AiEAnR6xrk7OCk91ymtzU+duZXDqDq35w0oO+MM8nqpac4YCIED+6c9dJKvRCc/C -nP8PMxRaUsbQet1woE7Fckn5tK4N +SM49AwEHA0IABCSif6O54Y74VpVNMHl37iECM1RKCjzJgu7a3CE2O8W7IdO3vQVT +X4FTcGjzR+WaQNdssuAJ8ch5lxDbOQDPT4WjYzBhMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTlu+78yKKKOakqZzrIgHoWLRMc/TAf +BgNVHSMEGDAWgBTiFM2bGqxEdRnw2T+bTdEejocbIDAKBggqhkjOPQQDAgNIADBF +AiB7bkMezoRqrjFzmQNzZ2smcrmKJ2ePrfNe3xFyWgQyWwIhAIIMAN2jg39P27mn +r0/T4PhfiLBY8naJw7t3bW6siCaU -----END CERTIFICATE----- ` testValidIntermediateCert = ` -----BEGIN CERTIFICATE----- MIIBdTCCARqgAwIBAgICEAAwCgYIKoZIzj0EAwIwIjEgMB4GA1UEAxMXVHJ1c3Rl -ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDcxODAz -MjFaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw -EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5SVyYjNRuuFXGjEmCcuVtMq7e2bmndPK -bRJ7lJ5cc0kZSoNJes5wXOtGRFbx3+admRHq+w1XEBXOe+yRUB8kdKM4MDYwEwYD -VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUKRjZnCyzN7x4jgA2Bjw0C/AI -ZMgwCgYIKoZIzj0EAwIDSQAwRgIhAMj0O2wDRLoxGIPUDOmUfYxmxglOecQhSkWO -NBtItSxmAiEAy0XCzvpL6XOZU3zxyCjTdJQa2RiC6YnypMaCaETzCaU= +ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zNDA3MjMxODA3 +NDlaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErSXc3EmG1iX8r1zra1v+wOFlX3nBTj3N +g03l5a5BvAclZ4jRLyU25RAT+LoPZwCag526xCsXQ2rctamWYuftvqM4MDYwEwYD +VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAU5bvu/MiiijmpKmc6yIB6Fi0T +HP0wCgYIKoZIzj0EAwIDSQAwRgIhAOmeRu2zAnCYHUhdBlH1/+K6pHysCJJHboSO +781NdfTkAiEA/ABjaj2sNNUFDCl/Ayqu7ufv+o5/F6mx3tMdnWZ0rlQ= -----END CERTIFICATE----- ` testValidCertWithDNSSANs = ` -----BEGIN CERTIFICATE----- -MIIBlTCCATugAwIBAgICEAQwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +MIIBljCCATugAwIBAgICEAQwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAYMRYw FAYDVQQDEw1jbGllbnQgY2VydCAzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -LFai39b7TYauNjg4M58f0qY6jTC7xEOhE84wTTcevZvH/t2Y7U0BBNGkvpb14yxh -60vrRKZA9t9G6ZvWKcY/BKNxMG8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j -BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwNwYDVR0RBDAwLoIVYS5jbGllbnQz +n3orlbZpOzCKyYbyFwUWO/hYcI+uldw2Fczdas+9g02E5mtnZpprxFnnzMipF6X9 +PIOVTWEPJ7LRNIN2Bt8zLKNxMG8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwNwYDVR0RBDAwLoIVYS5jbGllbnQz LmV4YW1wbGUuY29tghViLmNsaWVudDMuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwID -SAAwRQIgBSw8MsKWPPcpGtuVpNJonTEthIOjIGXswxiYG49y2BECIQC5D1DCX/lY -KSwF4aapPx4906VujTL+Ehj8L5ImUYcPbA== +SQAwRgIhAI36+egFjZ3NtLlT6xcSpTJtd6dOw/uBXxXnIiYfgoV6AiEA8Eazupyl +xiUuFh7lEsr293NN0G7IW6tJW36bWTxYOf8= -----END CERTIFICATE----- ` testValidCertWithEmailSAN = ` -----BEGIN CERTIFICATE----- -MIIBezCCASKgAwIBAgICEAUwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +MIIBfDCCASKgAwIBAgICEAUwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAYMRYw FAYDVQQDEw1jbGllbnQgY2VydCA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -WvX8BnCrzUSpLrYka8ed+bz6/HoXUvq5nRqysKe0nGYSsXKRjxLdCG8AKsoGIQIv -KOQScf/4TJUNIUY4XOsFI6NYMFYwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j -BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwHgYDVR0RBBcwFYETY2xpZW50NEBl -eGFtcGxlLmNvbTAKBggqhkjOPQQDAgNHADBEAiAMYGTjUBqgnai8UL3B/iQkCkMb -xgCC1ZYdZaJ1RBwFfgIgIhjQZ2s6dTaah/LzYJ9ZwMvSA86XQvzTVSuT6s+RJw0= +IIdyrCKf7142FXGd1ZU+eKkUu73heahK7iWOpCC3gMks12KKnpkcrt5ezQ1LM51P +CI/yWYdmLPHgmzychJIiF6NYMFYwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwHgYDVR0RBBcwFYETY2xpZW50NEBl +eGFtcGxlLmNvbTAKBggqhkjOPQQDAgNIADBFAiBeMogtQTVB9vi+snua/DfcKu18 +puJx/UGqXfwpYMObiwIhANwPL6FgGC208+LVca6r09BCh7QsoJCwCdAMTd5V7Pup -----END CERTIFICATE----- ` testValidCertWithIPSAN = ` -----BEGIN CERTIFICATE----- MIIBbTCCAROgAwIBAgICEAYwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAYMRYw FAYDVQQDEw1jbGllbnQgY2VydCA1MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -6+9BE+aomrR2Mdnx4iFY63t0hsVD6rYaHBW1b9roFQX6Cor4YeUfkEEF4LrGeAyb -wcqb6G1ExgNyjEh10Ai1M6NJMEcwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j -BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwDwYDVR0RBAgwBocEwKgKCjAKBggq -hkjOPQQDAgNIADBFAiEA/mq32YZZAacOH/P/wjvfD1n74DD/GkhW4kfS72Z0oGQC -IAQ+L8E78JOLaPWXiL7WFpVrb0hOHkV2m9Qw4GB41mUN +9JayRtfR5I0U6pk/FMf+LwdAY1+toL0CBwgj6UOgRPQKj2osSwJvGSQg3OBMyoMW +48UoykPMuEx9RgJAdG2rLaNJMEcwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwDwYDVR0RBAgwBocEwKgKCjAKBggq +hkjOPQQDAgNIADBFAiBxtRMWbiwa0kSftQ7KWymaEQzTNwa4DlanaJFitHX7wwIh +ALbkwkvT/egNexdQIL8SpP0owUiXvuB/OkmY49XQigub -----END CERTIFICATE----- ` testValidCertWithURISAN = ` -----BEGIN CERTIFICATE----- MIIBhTCCASugAwIBAgICEAcwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAYMRYw FAYDVQQDEw1jbGllbnQgY2VydCA2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE -7mHladapSY5PlVYToL1dHr0tJPGe5XVP8DSZJz+WWyS9tWsuEsK6P5yeZrbWASOX -foH7iVIdx3DMyukGsvMX+KNhMF8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j -BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwJwYDVR0RBCAwHoYcc3BpZmZlOi8v -ZXhhbXBsZS5jb20vZm9vL2JhcjAKBggqhkjOPQQDAgNIADBFAiAhzElKeGJzp2zP -GOTUEy0f6b2tvMYGDLQxCcp4bc4QuQIhAPwX4Y3Cr7uazQlbwL6D51y9NCcDyj3D -Z18vZNxm9ZR1 +vpHXNca1VPMtp5x3cAgAUjfGEkrJ3oaQmRvbPnbOuxP91GSh1qFDOcnQlLQSI9WV +HbaWsCYEQ/cEvCL45/PDqqNhMF8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwJwYDVR0RBCAwHoYcc3BpZmZlOi8v +ZXhhbXBsZS5jb20vZm9vL2JhcjAKBggqhkjOPQQDAgNIADBFAiEA0uz8g6/A+9p9 +AnNH20Sk6UiXwuKvy1pxk7TeMd02o6ACIBMsLVs+WIWZLht52+FEUJbpNXSF+IY+ +e4rx7Ac0KxRL +-----END CERTIFICATE----- +` + testValidCertWithUPNSAN = ` +-----BEGIN CERTIFICATE----- +MIIBiDCCAS2gAwIBAgICEAcwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzQwNzIzMTgwNzQ5WjAYMRYw +FAYDVQQDEw1jbGllbnQgY2VydCA3MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +QfxgDFsW27yUCmT990CwIVQexBdQ+dYRWuOHCnC+dGYESUcU6913hIEZEOwuqvwm +eKCX+NOZ+kXiGSxqD+F7C6NjMGEwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAU4hTNmxqsRHUZ8Nk/m03RHo6HGyAwKQYDVR0RAQH/BB8wHaAbBgorBgEE +AYI3FAIDoA0MC3Rlc3RfZGV2aWNlMAoGCCqGSM49BAMCA0kAMEYCIQDw8sM7Eu5F +hTgx7XMlMsIlUTs0/n1WrMlr5RcdPlG9tgIhAK9o4Dtlr2bqW7w9RSidNLT6loCJ +dgwikvJkMOfcuexx -----END CERTIFICATE----- ` ) @@ -366,6 +380,25 @@ func Test_isValidClientCertificate(t *testing.T) { assert.NoError(t, err, "should not return an error") assert.False(t, valid, "should return false") }) + t.Run("UserPrincipalName SAN", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithUPNSAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeUserPrincipalName: regexp.MustCompile(`^test_device$`), + }}) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithURISAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeDNS: regexp.MustCompile(`^test-device$`), // mismatched type + }}) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) } func TestClientCertConstraintsFromConfig(t *testing.T) { @@ -452,3 +485,147 @@ func TestClientCertConstraintsFromConfig(t *testing.T) { require.Error(t, err) }) } + +func TestGetUserPrincipalNamesFromSAN(t *testing.T) { + type OtherName[T any] struct { + TypeID asn1.ObjectIdentifier + Value T `asn1:"tag:0"` + } + type PrintableString struct { + Value string + } + type IA5String struct { + Value string `asn1:"ia5"` + } + type UTF8String struct { + Value string `asn1:"utf8"` + } + upn := func(name string) OtherName[UTF8String] { + return OtherName[UTF8String]{ + TypeID: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 20, 2, 3}, + Value: UTF8String{name}, + } + } + + t.Run("OneUserPrincipalName", func(t *testing.T) { + type SAN struct { + UPN OtherName[UTF8String] `asn1:"tag:0"` + } + san, err := asn1.Marshal(SAN{upn("hello")}) + require.NoError(t, err) + + names, err := getUserPrincipalNamesFromSAN(san) + assert.NoError(t, err) + assert.Equal(t, []string{"hello"}, names) + }) + t.Run("MultipleUserPrincipalNames", func(t *testing.T) { + type SAN struct { + UPN1 OtherName[UTF8String] `asn1:"tag:0"` + UPN2 OtherName[UTF8String] `asn1:"tag:0"` + UPN3 OtherName[UTF8String] `asn1:"tag:0"` + } + san, err := asn1.Marshal(SAN{upn("foo"), upn("bar"), upn("baz")}) + require.NoError(t, err) + + names, err := getUserPrincipalNamesFromSAN(san) + assert.NoError(t, err) + assert.Equal(t, []string{"foo", "bar", "baz"}, names) + }) + t.Run("NoUserPrincipalName", func(t *testing.T) { + type SAN struct { + DNS string `asn1:"tag:2,ia5"` + IP []byte `asn1:"tag:7"` + Email string `asn1:"tag:1,ia5"` + } + san, err := asn1.Marshal(SAN{ + "localhost", + []byte{127, 0, 0, 1}, + "me@example.com", + }) + require.NoError(t, err) + + names, err := getUserPrincipalNamesFromSAN(san) + assert.NoError(t, err) + assert.Empty(t, names) + }) + t.Run("MultipleNameTypes", func(t *testing.T) { + type SAN struct { + DNS1 string `asn1:"tag:2,ia5"` + DNS2 string `asn1:"tag:2,ia5"` + Other1 OtherName[IA5String] `asn1:"tag:0"` + Other2 OtherName[UTF8String] `asn1:"tag:0"` + Other3 OtherName[PrintableString] `asn1:"tag:0"` + } + san, err := asn1.Marshal(SAN{ + "example.com", + "example.org", + OtherName[IA5String]{ + TypeID: asn1.ObjectIdentifier{1, 1, 1, 1}, + Value: IA5String{"IA5String"}, + }, + upn("UserPrincipalName"), + OtherName[PrintableString]{ + TypeID: asn1.ObjectIdentifier{1, 1, 1, 2}, + Value: PrintableString{"PrintableString"}, + }, + }) + require.NoError(t, err) + + names, err := getUserPrincipalNamesFromSAN(san) + assert.NoError(t, err) + assert.Equal(t, []string{"UserPrincipalName"}, names) + }) + t.Run("UserPrincipalNameWrongValueType", func(t *testing.T) { + type SAN struct { + UPN OtherName[PrintableString] `asn1:"tag:0"` + } + san, err := asn1.Marshal(SAN{ + OtherName[PrintableString]{ + TypeID: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 20, 2, 3}, + Value: PrintableString{"PrintableString"}, + }, + }) + require.NoError(t, err) + + names, err := getUserPrincipalNamesFromSAN(san) + assert.ErrorContains(t, err, "expected UTF8String") + assert.Empty(t, names) + }) + t.Run("EmptySAN", func(t *testing.T) { + names, err := getUserPrincipalNamesFromSAN(nil) + assert.ErrorContains(t, err, "error reading GeneralNames sequence") + assert.Empty(t, names) + }) + t.Run("TruncatedGeneralName", func(t *testing.T) { + san := []byte{0x30, 0x02, 0x82, 0x05 /* 5 more bytes expected */} + names, err := getUserPrincipalNamesFromSAN(san) + assert.ErrorContains(t, err, "error reading GeneralName") + assert.Empty(t, names) + }) + t.Run("OtherNameWrongTypeIDType", func(t *testing.T) { + san := []byte{0x30, 0x06, 0xa0, 0x04, 0x02 /* type Integer, not OID */, 0x02, 0x46, 0x01} + names, err := getUserPrincipalNamesFromSAN(san) + assert.ErrorContains(t, err, "error reading OtherName type ID") + assert.Empty(t, names) + }) + t.Run("UserPrincipalNameWrongValueTag", func(t *testing.T) { + type BadOtherName struct { + TypeID asn1.ObjectIdentifier + Value UTF8String `asn1:"tag:1"` // instead of tag 0 + } + type SAN struct { + UPN BadOtherName `asn1:"tag:0"` + } + san, err := asn1.Marshal(SAN{ + UPN: BadOtherName{ + TypeID: oidUserPrincipalName, + Value: UTF8String{"hello"}, + }, + }) + require.NoError(t, err) + + names, err := getUserPrincipalNamesFromSAN(san) + assert.ErrorContains(t, err, "error reading UserPrincipalName value") + assert.Empty(t, names) + }) +} diff --git a/authorize/evaluator/gen-test-certs.go b/authorize/evaluator/gen-test-certs.go index 0d4f3a7cf..aeb43abda 100644 --- a/authorize/evaluator/gen-test-certs.go +++ b/authorize/evaluator/gen-test-certs.go @@ -8,6 +8,7 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "encoding/pem" "fmt" "log" @@ -68,6 +69,33 @@ func newCRL( return string(pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: der})) } +// Returns a raw SubjectAltName extension with a single UserPrincipalName. +func newSANUserPrincipalName(upnValue string) []byte { + type UPN struct { + UTF8String string `asn1:"utf8"` + } + type OtherName struct { + OID asn1.ObjectIdentifier + Value UPN `asn1:"tag:0"` + } + type GeneralNames struct { + OtherName OtherName `asn1:"tag:0"` + } + san, err := asn1.Marshal(GeneralNames{ + OtherName: OtherName{ + OID: asn1.ObjectIdentifier{ + 1, 3, 6, 1, 4, 1, 311, 20, 2, 3}, + Value: UPN{ + UTF8String: upnValue, + }, + }, + }) + if err != nil { + log.Fatalln(err) + } + return san +} + // Generates new test certificates and CRLs. func main() { notAfter := time.Now().Add(3650 * 24 * time.Hour) @@ -190,6 +218,20 @@ func main() { ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, rootCA, rootKey) + trustedClientCert7PEM, _, _ := newCertificate(&x509.Certificate{ + SerialNumber: big.NewInt(0x1007), + Subject: pkix.Name{ + CommonName: "client cert 7", + }, + NotAfter: notAfter, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + ExtraExtensions: []pkix.Extension{{ + Id: asn1.ObjectIdentifier{2, 5, 29, 17}, + Critical: true, // requires special handling during verification + Value: newSANUserPrincipalName("test_device"), + }}, + }, rootCA, rootKey) + fmt.Println(` const ( testCA = ` + "`\n" + rootPEM + "`" + ` @@ -203,6 +245,7 @@ const ( testValidCertWithEmailSAN = ` + "`\n" + trustedClientCert4PEM + "`" + ` testValidCertWithIPSAN = ` + "`\n" + trustedClientCert5PEM + "`" + ` testValidCertWithURISAN = ` + "`\n" + trustedClientCert6PEM + "`" + ` + testValidCertWithUPNSAN = ` + "`\n" + trustedClientCert7PEM + "`" + ` ) `) } diff --git a/authorize/evaluator/headers_evaluator_test.go b/authorize/evaluator/headers_evaluator_test.go index 64c8e4940..8cbef3018 100644 --- a/authorize/evaluator/headers_evaluator_test.go +++ b/authorize/evaluator/headers_evaluator_test.go @@ -171,7 +171,7 @@ func TestHeadersEvaluator(t *testing.T) { assert.Equal(t, "CUSTOM_VALUE", output.Headers.Get("X-Custom-Header")) assert.Equal(t, "ID_TOKEN", output.Headers.Get("X-ID-Token")) assert.Equal(t, "ACCESS_TOKEN", output.Headers.Get("X-Access-Token")) - assert.Equal(t, "ebf421e323e31c3900a7985a16e72c59f45f5a2c15283297567e226b3b17d1a1", + assert.Equal(t, "3febe6467787e93f0a01030e0803072feaa710f724a9dc74de05cfba3d4a6d23", output.Headers.Get("Client-Cert-Fingerprint")) assert.Equal(t, "escaped $dollar sign", output.Headers.Get("Foo")) }) diff --git a/config/envoyconfig/listeners_test.go b/config/envoyconfig/listeners_test.go index dec1c0f59..5a993414f 100644 --- a/config/envoyconfig/listeners_test.go +++ b/config/envoyconfig/listeners_test.go @@ -278,6 +278,7 @@ func Test_buildDownstreamTLSContext(t *testing.T) { {Type: config.SANTypeEmail, Pattern: `.*@example\.com`}, {Type: config.SANTypeIPAddress, Pattern: `10\.10\.42\..*`}, {Type: config.SANTypeURI, Pattern: `spiffe://example\.com/.*`}, + {Type: config.SANTypeUserPrincipalName, Pattern: `^device-id$`}, }, }, }} @@ -321,6 +322,16 @@ func Test_buildDownstreamTLSContext(t *testing.T) { } }, "sanType": "URI" + }, + { + "matcher": { + "safeRegex": { + "googleRe2": {}, + "regex": "^device-id$" + } + }, + "sanType": "OTHER_NAME", + "oid": "1.3.6.1.4.1.311.20.2.3" } ], "onlyVerifyLeafCertCrl": true, diff --git a/config/mtls.go b/config/mtls.go index bfb0d9cb5..9c1aaa16c 100644 --- a/config/mtls.go +++ b/config/mtls.go @@ -51,6 +51,10 @@ const ( // SANTypeURI represents a URI. SANTypeURI SANType = "uri" + + // SANTypeUserPrincipalName represents a UserPrincipalName (otherName with + // type ID 1.3.6.1.4.1.311.20.2.3). + SANTypeUserPrincipalName = "user_principal_name" ) // DownstreamMTLSSettings specify the downstream client certificate requirements. @@ -259,6 +263,7 @@ func (s *SANMatcher) ToEnvoyProto() *envoy_tls.SubjectAltNameMatcher { }, }, }, + Oid: s.oid(), } } @@ -272,7 +277,18 @@ func (s *SANMatcher) envoyType() envoy_tls.SubjectAltNameMatcher_SanType { return envoy_tls.SubjectAltNameMatcher_IP_ADDRESS case SANTypeURI: return envoy_tls.SubjectAltNameMatcher_URI + case SANTypeUserPrincipalName: + return envoy_tls.SubjectAltNameMatcher_OTHER_NAME default: return envoy_tls.SubjectAltNameMatcher_SAN_TYPE_UNSPECIFIED } } + +func (s *SANMatcher) oid() string { + switch s.Type { + case SANTypeUserPrincipalName: + return "1.3.6.1.4.1.311.20.2.3" + default: + return "" + } +} diff --git a/config/mtls_test.go b/config/mtls_test.go index f1d4d59b8..6482f93da 100644 --- a/config/mtls_test.go +++ b/config/mtls_test.go @@ -178,6 +178,7 @@ func TestDownstreamMTLSSettingsValidate(t *testing.T) { {Type: "email", Pattern: `.*@\.example\.com`}, {Type: "ip_address", Pattern: `192\.168\.0\..*`}, {Type: "uri", Pattern: `spiffe://example.com/department/.*`}, + {Type: "user_principal_name", Pattern: `username@realm`}, }, }, ""}, } diff --git a/go.mod b/go.mod index 9fa237241..c7786cda2 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/cloudflare/circl v1.3.9 github.com/coreos/go-oidc/v3 v3.10.0 github.com/docker/docker v27.0.3+incompatible - github.com/envoyproxy/go-control-plane v0.12.0 + github.com/envoyproxy/go-control-plane v0.12.1-0.20240717153332-b7af5b7dab54 github.com/envoyproxy/protoc-gen-validate v1.0.4 github.com/go-chi/chi/v5 v5.1.0 github.com/go-jose/go-jose/v3 v3.0.3 @@ -85,7 +85,7 @@ require ( golang.org/x/time v0.5.0 google.golang.org/api v0.183.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 - google.golang.org/grpc v1.64.1 + google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 namespacelabs.dev/go-filenotify v0.0.0-20220511192020-53ea11be7eaa @@ -93,6 +93,7 @@ require ( ) require ( + cel.dev/expr v0.15.0 // indirect cloud.google.com/go v0.114.0 // indirect cloud.google.com/go/auth v0.5.1 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect @@ -125,7 +126,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/caddyserver/zerossl v0.1.3 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect - github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 // indirect + github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect github.com/containerd/continuity v0.4.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.5.0 // indirect @@ -182,6 +183,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/philhofer/fwd v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect diff --git a/go.sum b/go.sum index 281d7be37..7d707f3b5 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +cel.dev/expr v0.15.0 h1:O1jzfJCQBfL5BFoYktaxwIhuttaQPsVWerH9/EEKx0w= +cel.dev/expr v0.15.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -158,8 +160,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc= -github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= +github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw= +github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -198,8 +200,8 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= -github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240717153332-b7af5b7dab54 h1:ekmHq5UzNblkBSc6SUen5FKxnq1h30JbZWbQFNzWLPI= +github.com/envoyproxy/go-control-plane v0.12.1-0.20240717153332-b7af5b7dab54/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= @@ -262,8 +264,8 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= +github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -509,6 +511,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1025,8 +1029,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=