Skip to content

Commit af8ba60

Browse files
committed
Add path exclusion support to mTLS authentication
Signed-off-by: Kacper Rzetelski <[email protected]>
1 parent 841a714 commit af8ba60

File tree

8 files changed

+667
-34
lines changed

8 files changed

+667
-34
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIB3DCCAWGgAwIBAgIUJVN8KehL1MmccvLb/mHthSMfnnswCgYIKoZIzj0EAwIw
3+
EDEOMAwGA1UEAwwFdGVzdDMwIBcNMjMwMTEwMTgxMTAwWhgPMjEyMjEyMTcxODEx
4+
MDBaMBAxDjAMBgNVBAMMBXRlc3QzMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEf8wC
5+
qU9e4lPZZqJMA4nJ84rLPdfryoUI8tquBAHtae4yfXP3z6Hz92XdPaS4ZAFDjTLt
6+
Jsl45KYixNb7y9dtbVoNxNxdDC4ywaoklqkpBGY0I9GEpNzaBll/4DIJvGcgo3ow
7+
eDAdBgNVHQ4EFgQUvyvu/TnJyRS7OGdujTbWM/W07yMwHwYDVR0jBBgwFoAUvyvu
8+
/TnJyRS7OGdujTbWM/W07yMwDwYDVR0TAQH/BAUwAwEB/zAQBgNVHREECTAHggV0
9+
ZXN0MzATBgNVHSUEDDAKBggrBgEFBQcDAjAKBggqhkjOPQQDAgNpADBmAjEAt7HK
10+
knE2MzwZ2B2dgn1/q3ikWDiO20Hbd97jo3tmv87FcF2vMqqJpHjcldJqplfsAjEA
11+
sfAz49y6Sf6LNlNS+Fc/lbOOwcrlzC+J5GJ8OmNoQPsvvDvhzGbwFiVw1M2uMqtG
12+
-----END CERTIFICATE-----
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBxzCCAU2gAwIBAgIUGCNnsX0qd0HD7UaQsx67ze0UaNowCgYIKoZIzj0EAwIw
3+
DzENMAsGA1UEAwwEdGVzdDAgFw0yMTA4MjAxNDQ5MTRaGA8yMTIxMDcyNzE0NDkx
4+
NFowDzENMAsGA1UEAwwEdGVzdDB2MBAGByqGSM49AgEGBSuBBAAiA2IABLFRLjQB
5+
XViHUAEIsKglwb0HxPC/+CDa1TTOp1b0WErYW7Xcx5mRNEksVWAXOWYKPej10hfy
6+
JSJE/2NiRAbrAcPjiRv01DgDt+OzwM4A0ZYqBj/3qWJKH/Kc8oKhY41bzKNoMGYw
7+
HQYDVR0OBBYEFPRbKtRBgw+AZ0b6T8oWw/+QoyjaMB8GA1UdIwQYMBaAFPRbKtRB
8+
gw+AZ0b6T8oWw/+QoyjaMA8GA1UdEwEB/wQFMAMBAf8wEwYDVR0lBAwwCgYIKwYB
9+
BQUHAwIwCgYIKoZIzj0EAwIDaAAwZQIwZqwXMJiTycZdmLN+Pwk/8Sb7wQazbocb
10+
16Zw5mZXqFJ4K+74OQMZ33i82hYohtE/AjEAn0a8q8QupgiXpr0I/PvGTRKqLQRM
11+
0mptBvpn/DcB2p3Hi80GJhtchz9Z0OqbMX4S
12+
-----END CERTIFICATE-----

web/authentication/x509/x509.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2023 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package x509
15+
16+
import (
17+
"crypto/tls"
18+
"crypto/x509"
19+
"errors"
20+
"net/http"
21+
22+
"github.com/prometheus/exporter-toolkit/web/authentication"
23+
)
24+
25+
const (
26+
denyReasonCertificateRequired = "certificate required"
27+
denyReasonBadCertificate = "bad certificate"
28+
denyReasonUnknownCA = "unknown certificate authority"
29+
denyReasonCertificateExpired = "expired certificate"
30+
)
31+
32+
type X509Authenticator struct {
33+
requireClientCerts bool
34+
35+
// verifyOptions returns VerifyOptions used to obtain parameters for Certificate.Verify.
36+
verifyOptions func() x509.VerifyOptions
37+
38+
// verifyPeerCertificate corresponds to `VerifyPeerCertificate` from crypto/tls Config: https://pkg.go.dev/crypto/tls#Config.
39+
// It bears the same semantics.
40+
verifyPeerCertificate func([][]byte, [][]*x509.Certificate) error
41+
}
42+
43+
func (x *X509Authenticator) Authenticate(r *http.Request) (bool, string, *authentication.HTTPChallenge, error) {
44+
if r.TLS == nil {
45+
return false, "", nil, errors.New("no tls connection state in request")
46+
}
47+
48+
if len(r.TLS.PeerCertificates) == 0 && x.requireClientCerts {
49+
if r.TLS.Version == tls.VersionTLS13 {
50+
return false, denyReasonCertificateRequired, nil, nil
51+
}
52+
53+
return false, denyReasonBadCertificate, nil, nil
54+
}
55+
56+
var verifiedChains [][]*x509.Certificate
57+
if len(r.TLS.PeerCertificates) > 0 && x.verifyOptions != nil {
58+
opts := x.verifyOptions()
59+
if opts.Intermediates == nil && len(r.TLS.PeerCertificates) > 1 {
60+
opts.Intermediates = x509.NewCertPool()
61+
for _, cert := range r.TLS.PeerCertificates[1:] {
62+
opts.Intermediates.AddCert(cert)
63+
}
64+
}
65+
66+
chains, err := r.TLS.PeerCertificates[0].Verify(opts)
67+
if err != nil {
68+
if errors.As(err, &x509.UnknownAuthorityError{}) {
69+
return false, denyReasonUnknownCA, nil, nil
70+
}
71+
72+
var errCertificateInvalid x509.CertificateInvalidError
73+
if errors.As(err, &errCertificateInvalid) && errCertificateInvalid.Reason == x509.Expired {
74+
return false, denyReasonCertificateExpired, nil, nil
75+
}
76+
77+
return false, denyReasonBadCertificate, nil, nil
78+
}
79+
80+
verifiedChains = chains
81+
}
82+
83+
if x.verifyPeerCertificate != nil {
84+
rawCerts := make([][]byte, 0, len(r.TLS.PeerCertificates))
85+
for _, c := range r.TLS.PeerCertificates {
86+
rawCerts = append(rawCerts, c.Raw)
87+
}
88+
89+
err := x.verifyPeerCertificate(rawCerts, verifiedChains)
90+
if err != nil {
91+
return false, denyReasonBadCertificate, nil, nil
92+
}
93+
}
94+
95+
return true, "", nil, nil
96+
}
97+
98+
func NewX509Authenticator(requireClientCerts bool, verifyOptions func() x509.VerifyOptions, verifyPeerCertificate func([][]byte, [][]*x509.Certificate) error) authentication.Authenticator {
99+
return &X509Authenticator{
100+
requireClientCerts: requireClientCerts,
101+
verifyOptions: verifyOptions,
102+
verifyPeerCertificate: verifyPeerCertificate,
103+
}
104+
}
105+
106+
var _ authentication.Authenticator = &X509Authenticator{}

0 commit comments

Comments
 (0)