mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-30 19:06:33 +02:00
Add a new max_verify_depth option to the downstream_mtls settings group, with a default value of 1 (to match the behavior of current Pomerium releases). Populate the corresponding setting within Envoy, and also implement a depth check within isValidClientCertificate() in the authorize service.
204 lines
8.1 KiB
Go
204 lines
8.1 KiB
Go
package evaluator
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// These certificates can be regenerated by running:
|
|
//
|
|
// go run ./gen-test-certs.go
|
|
//
|
|
// (Copy and paste the output here.)
|
|
const (
|
|
testCA = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBaTCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAaMRgw
|
|
FgYDVQQDEw9UcnVzdGVkIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
|
|
AATcFCe6i6IqnevuUoR8nTrka8fikGYB3ciKfRyS0NUfm27MGsbuU2ribMYjhuz2
|
|
K4/nU7A2hcu393JNKriXgwoyo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
|
|
BAUwAwEB/zAdBgNVHQ4EFgQU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0E
|
|
AwIDSQAwRgIhAJIFP4r9Gbo0D2MM/fzPx5wtXsjH1IoQMpn0aw+G1WkmAiEAi56g
|
|
gO7l3bJj1YZtBv3tEkZPzaZ+xL3Nllcjv1K12Ac=
|
|
-----END CERTIFICATE-----
|
|
`
|
|
testValidCert = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBYTCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAeMRww
|
|
GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
|
AQcDQgAE3xjPIJPp9v+qu89DNHnqcIfkOSkLb8irRAnFmAYdJJLRqWRNRjmzRHtZ
|
|
htT4TWvhEw6VsFbRlsd510+dEASm9aM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
|
|
HwYDVR0jBBgwFoAU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0EAwIDRwAw
|
|
RAIgYZaCYXhfBBR8w/AfqUm9MVLYrgkz78mndNFFjz+YvpwCIFsfyIjft/vRcuaU
|
|
xlJFtrmFMSt4x1TecZfJsWDA0M55
|
|
-----END CERTIFICATE-----
|
|
`
|
|
testUntrustedCert = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBZzCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz
|
|
dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDYyMTEzMjRaMCAx
|
|
HjAcBgNVBAMTFXVudHJ1c3RlZCBjbGllbnQgY2VydDBZMBMGByqGSM49AgEGCCqG
|
|
SM49AwEHA0IABCLf7g3vlTE+xuIDttn/FYMpAAzlCKqr37rzBnBtuEhCDypW6Y/Y
|
|
MbIboFx9o2M9FYLzDJCS7Bj3cp2qd145HdqjODA2MBMGA1UdJQQMMAoGCCsGAQUF
|
|
BwMCMB8GA1UdIwQYMBaAFGGTTkLU8r+tXUCs+nMR5Xm3KkgLMAoGCCqGSM49BAMC
|
|
A0kAMEYCIQCELFkfQak8X+Yvc7H95j+shSnVgwUuOYT1Lv2NLT1qPAIhAMEvKiwc
|
|
ZkWK0F4PJLpSt1wsbnVtfK7kXwHxdp2Z8yy7
|
|
-----END CERTIFICATE-----
|
|
`
|
|
testRevokedCert = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBYTCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAeMRww
|
|
GgYDVQQDExNyZXZva2VkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
|
|
AQcDQgAENFshpve+if3UmlNyPi5sVz8A03F9AAt6u1LxqiR5cMO6eU+L91Bey/XC
|
|
XYrqgpJzbRgTuC4LCFx6cwwl5ff/4KM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw
|
|
HwYDVR0jBBgwFoAU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0EAwIDRwAw
|
|
RAIgappua0SnpXDzp9nml4iHqKtYAHTn/rg0w405ahdqBQwCIHulKmPGFNLDw1dq
|
|
1bZyKsG1t58DfFsO9G27sRssvCgV
|
|
-----END CERTIFICATE-----
|
|
`
|
|
testCRL = `
|
|
-----BEGIN X509 CRL-----
|
|
MIHeMIGFAgEBMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0ZWQgUm9vdCBD
|
|
QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTIzMDgwOTIxMTMyNFqgMDAuMB8G
|
|
A1UdIwQYMBaAFNEl9Mb/X+oGAHCwvdus/gbfb0wDMAsGA1UdFAQEAgIgADAKBggq
|
|
hkjOPQQDAgNIADBFAiEA1QoleqO9qKhxSxKUc+SOQlFTG9sTbs3ztniUhi0CxhAC
|
|
IBElm/lXpVVuWrt0PJhcQHhHqbOxnfkx3HUxVEBWMOzX
|
|
-----END X509 CRL-----
|
|
`
|
|
testIntermediateCA = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBkjCCATegAwIBAgICEAMwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl
|
|
ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAiMSAw
|
|
HgYDVQQDExdUcnVzdGVkIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqG
|
|
SM49AwEHA0IABJyxF8EUBMMh/avAul6M8AjoKstuIULPIHOvjYftT/hSqGHNYM6g
|
|
0NIBW1g2QX/fnHG9tBy45ReTkVY5HMoO2wujYzBhMA4GA1UdDwEB/wQEAwIBBjAP
|
|
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRe0Fh8zjBKzxms/xpbS/vHY5hqujAf
|
|
BgNVHSMEGDAWgBTRJfTG/1/qBgBwsL3brP4G329MAzAKBggqhkjOPQQDAgNJADBG
|
|
AiEAsnob34JrBGbJjoTZS84mfno2Vb+QPJ1xy3U7AbgyYM4CIQCL3P2A3w1Z87Nr
|
|
0A/i8rXw+kiGP1OHbs4k85ZIg6FAtQ==
|
|
-----END CERTIFICATE-----
|
|
`
|
|
testValidIntermediateCert = `
|
|
-----BEGIN CERTIFICATE-----
|
|
MIIBdTCCARqgAwIBAgICEAAwCgYIKoZIzj0EAwIwIjEgMB4GA1UEAxMXVHJ1c3Rl
|
|
ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDYyMTEz
|
|
MjRaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw
|
|
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvv9cmqtcuPuSXv6Qup0ycveDtx4PYC4V
|
|
UKp5BU1B/1h/IupoIlX165rERkNCxyDXjfw5zkcHXsP1qRbc3LSXT6M4MDYwEwYD
|
|
VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUXtBYfM4wSs8ZrP8aW0v7x2OY
|
|
arowCgYIKoZIzj0EAwIDSQAwRgIhAJJwdjSiC3avGDc1KZo/AZ1cMPDcFZkI92E6
|
|
BVAnH/e8AiEAjy8cP1msG62BeDaAVU5NcU9RAXDw1Oz4HkpELXQWqK8=
|
|
-----END CERTIFICATE-----
|
|
`
|
|
)
|
|
|
|
func Test_isValidClientCertificate(t *testing.T) {
|
|
var noConstraints ClientCertConstraints
|
|
t.Run("no ca", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(
|
|
"", "", ClientCertificateInfo{Leaf: "WHATEVER!"}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.True(t, valid, "should return true")
|
|
})
|
|
t.Run("no cert", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.False(t, valid, "should return false")
|
|
})
|
|
t.Run("valid cert", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testValidCert,
|
|
}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.True(t, valid, "should return true")
|
|
})
|
|
t.Run("valid cert with intermediate", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testValidIntermediateCert,
|
|
Intermediates: testIntermediateCA,
|
|
}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.True(t, valid, "should return true")
|
|
})
|
|
t.Run("valid cert missing intermediate", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testValidIntermediateCert,
|
|
Intermediates: "",
|
|
}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.False(t, valid, "should return false")
|
|
})
|
|
t.Run("intermediate CA as root", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testIntermediateCA, "", ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testValidIntermediateCert,
|
|
}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.True(t, valid, "should return true")
|
|
})
|
|
t.Run("unsigned cert", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testUntrustedCert,
|
|
}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.False(t, valid, "should return false")
|
|
})
|
|
t.Run("not a cert", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: "WHATEVER!",
|
|
}, noConstraints)
|
|
assert.Error(t, err, "should return an error")
|
|
assert.False(t, valid, "should return false")
|
|
})
|
|
t.Run("revoked cert", func(t *testing.T) {
|
|
revokedCertInfo := ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testRevokedCert,
|
|
}
|
|
|
|
// The "revoked cert" should otherwise be valid (when no CRL is specified).
|
|
valid, err := isValidClientCertificate(testCA, "", revokedCertInfo, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.True(t, valid, "should return true")
|
|
|
|
valid, err = isValidClientCertificate(testCA, testCRL, revokedCertInfo, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.False(t, valid, "should return false")
|
|
|
|
// Specifying a CRL containing the revoked cert should not affect other certs.
|
|
valid, err = isValidClientCertificate(testCA, testCRL, ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testValidCert,
|
|
}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.True(t, valid, "should return true")
|
|
})
|
|
t.Run("missing CRL", func(t *testing.T) {
|
|
// If a CRL is provided for any CA, it must be provided for all CAs.
|
|
valid, err := isValidClientCertificate(testCA, testCRL, ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testValidIntermediateCert,
|
|
Intermediates: testIntermediateCA,
|
|
}, noConstraints)
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.False(t, valid, "should return false")
|
|
})
|
|
t.Run("chain too deep", func(t *testing.T) {
|
|
valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{
|
|
Presented: true,
|
|
Leaf: testValidIntermediateCert,
|
|
Intermediates: testIntermediateCA,
|
|
}, ClientCertConstraints{MaxVerifyDepth: 1})
|
|
assert.NoError(t, err, "should not return an error")
|
|
assert.False(t, valid, "should return false")
|
|
})
|
|
}
|