Skip to content

Commit 9705f1a

Browse files
committed
Add path exclusion support to mTLS authentication
1 parent afa4616 commit 9705f1a

8 files changed

+573
-29
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: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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/x509"
18+
"encoding/hex"
19+
"fmt"
20+
"net/http"
21+
"strings"
22+
23+
"github.com/prometheus/exporter-toolkit/web/authentication"
24+
)
25+
26+
type RequireClientCertsFunc func() bool
27+
28+
type VerifyOptionsFunc func() x509.VerifyOptions
29+
30+
type VerifyPeerCertificateFunc func([][]byte, [][]*x509.Certificate) error
31+
32+
type X509Authenticator struct {
33+
requireClientCertsFn RequireClientCertsFunc
34+
verifyOptionsFn VerifyOptionsFunc
35+
verifyPeerCertificateFn VerifyPeerCertificateFunc
36+
}
37+
38+
func columnSeparatedHex(d []byte) string {
39+
h := strings.ToUpper(hex.EncodeToString(d))
40+
var sb strings.Builder
41+
for i, r := range h {
42+
sb.WriteRune(r)
43+
if i%2 == 1 && i != len(h)-1 {
44+
sb.WriteRune(':')
45+
}
46+
}
47+
return sb.String()
48+
}
49+
50+
func certificateIdentifier(c *x509.Certificate) string {
51+
return fmt.Sprintf(
52+
"SN=%d, SKID=%s, AKID=%s",
53+
c.SerialNumber,
54+
columnSeparatedHex(c.SubjectKeyId),
55+
columnSeparatedHex(c.AuthorityKeyId),
56+
)
57+
}
58+
59+
func (x *X509Authenticator) Authenticate(r *http.Request) (bool, string, error) {
60+
if r.TLS == nil {
61+
return false, "No TLS connection state in request", nil
62+
}
63+
64+
if len(r.TLS.PeerCertificates) == 0 && x.requireClientCertsFn() {
65+
return false, "A certificate is required to be sent by the client.", nil
66+
}
67+
68+
var verifiedChains [][]*x509.Certificate
69+
if len(r.TLS.PeerCertificates) > 0 && x.verifyOptionsFn != nil {
70+
opts := x.verifyOptionsFn()
71+
if opts.Intermediates == nil && len(r.TLS.PeerCertificates) > 1 {
72+
opts.Intermediates = x509.NewCertPool()
73+
for _, cert := range r.TLS.PeerCertificates[1:] {
74+
opts.Intermediates.AddCert(cert)
75+
}
76+
}
77+
78+
chains, err := r.TLS.PeerCertificates[0].Verify(opts)
79+
if err != nil {
80+
return false, fmt.Sprintf("verifying certificate %s failed: %v", certificateIdentifier(r.TLS.PeerCertificates[0]), err), nil
81+
}
82+
83+
verifiedChains = chains
84+
}
85+
86+
if x.verifyPeerCertificateFn != nil {
87+
rawCerts := make([][]byte, 0, len(r.TLS.PeerCertificates))
88+
for _, c := range r.TLS.PeerCertificates {
89+
rawCerts = append(rawCerts, c.Raw)
90+
}
91+
92+
err := x.verifyPeerCertificateFn(rawCerts, verifiedChains)
93+
if err != nil {
94+
return false, fmt.Sprintf("verifying peer certificate failed: %v", err), nil
95+
}
96+
}
97+
98+
return true, "", nil
99+
}
100+
101+
func NewX509Authenticator(requireClientCertsFn RequireClientCertsFunc, verifyOptionsFn VerifyOptionsFunc, verifyPeerCertificateFn VerifyPeerCertificateFunc) authentication.Authenticator {
102+
return &X509Authenticator{
103+
requireClientCertsFn: requireClientCertsFn,
104+
verifyOptionsFn: verifyOptionsFn,
105+
verifyPeerCertificateFn: verifyPeerCertificateFn,
106+
}
107+
}
108+
109+
var _ authentication.Authenticator = &X509Authenticator{}
110+
111+
// DefaultVerifyOptions returns VerifyOptions that use the system root certificates, current time,
112+
// and requires certificates to be valid for client auth (x509.ExtKeyUsageClientAuth)
113+
func DefaultVerifyOptions() x509.VerifyOptions {
114+
return x509.VerifyOptions{
115+
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
116+
}
117+
}

0 commit comments

Comments
 (0)