Skip to content

Commit

Permalink
Add path exclusion support to mTLS authentication
Browse files Browse the repository at this point in the history
Signed-off-by: Kacper Rzetelski <[email protected]>
  • Loading branch information
rzetelskik committed May 11, 2023
1 parent 7e5b4ac commit ab8e6f5
Show file tree
Hide file tree
Showing 8 changed files with 573 additions and 29 deletions.
12 changes: 12 additions & 0 deletions web/authentication/x509/testdata/client2_selfsigned.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIB3DCCAWGgAwIBAgIUJVN8KehL1MmccvLb/mHthSMfnnswCgYIKoZIzj0EAwIw
EDEOMAwGA1UEAwwFdGVzdDMwIBcNMjMwMTEwMTgxMTAwWhgPMjEyMjEyMTcxODEx
MDBaMBAxDjAMBgNVBAMMBXRlc3QzMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEf8wC
qU9e4lPZZqJMA4nJ84rLPdfryoUI8tquBAHtae4yfXP3z6Hz92XdPaS4ZAFDjTLt
Jsl45KYixNb7y9dtbVoNxNxdDC4ywaoklqkpBGY0I9GEpNzaBll/4DIJvGcgo3ow
eDAdBgNVHQ4EFgQUvyvu/TnJyRS7OGdujTbWM/W07yMwHwYDVR0jBBgwFoAUvyvu
/TnJyRS7OGdujTbWM/W07yMwDwYDVR0TAQH/BAUwAwEB/zAQBgNVHREECTAHggV0
ZXN0MzATBgNVHSUEDDAKBggrBgEFBQcDAjAKBggqhkjOPQQDAgNpADBmAjEAt7HK
knE2MzwZ2B2dgn1/q3ikWDiO20Hbd97jo3tmv87FcF2vMqqJpHjcldJqplfsAjEA
sfAz49y6Sf6LNlNS+Fc/lbOOwcrlzC+J5GJ8OmNoQPsvvDvhzGbwFiVw1M2uMqtG
-----END CERTIFICATE-----
12 changes: 12 additions & 0 deletions web/authentication/x509/testdata/client_selfsigned.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBxzCCAU2gAwIBAgIUGCNnsX0qd0HD7UaQsx67ze0UaNowCgYIKoZIzj0EAwIw
DzENMAsGA1UEAwwEdGVzdDAgFw0yMTA4MjAxNDQ5MTRaGA8yMTIxMDcyNzE0NDkx
NFowDzENMAsGA1UEAwwEdGVzdDB2MBAGByqGSM49AgEGBSuBBAAiA2IABLFRLjQB
XViHUAEIsKglwb0HxPC/+CDa1TTOp1b0WErYW7Xcx5mRNEksVWAXOWYKPej10hfy
JSJE/2NiRAbrAcPjiRv01DgDt+OzwM4A0ZYqBj/3qWJKH/Kc8oKhY41bzKNoMGYw
HQYDVR0OBBYEFPRbKtRBgw+AZ0b6T8oWw/+QoyjaMB8GA1UdIwQYMBaAFPRbKtRB
gw+AZ0b6T8oWw/+QoyjaMA8GA1UdEwEB/wQFMAMBAf8wEwYDVR0lBAwwCgYIKwYB
BQUHAwIwCgYIKoZIzj0EAwIDaAAwZQIwZqwXMJiTycZdmLN+Pwk/8Sb7wQazbocb
16Zw5mZXqFJ4K+74OQMZ33i82hYohtE/AjEAn0a8q8QupgiXpr0I/PvGTRKqLQRM
0mptBvpn/DcB2p3Hi80GJhtchz9Z0OqbMX4S
-----END CERTIFICATE-----
117 changes: 117 additions & 0 deletions web/authentication/x509/x509.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2023 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package x509

import (
"crypto/x509"
"encoding/hex"
"fmt"
"net/http"
"strings"

"github.com/prometheus/exporter-toolkit/web/authentication"
)

type RequireClientCertsFunc func() bool

type VerifyOptionsFunc func() x509.VerifyOptions

type VerifyPeerCertificateFunc func([][]byte, [][]*x509.Certificate) error

type X509Authenticator struct {
requireClientCertsFn RequireClientCertsFunc
verifyOptionsFn VerifyOptionsFunc
verifyPeerCertificateFn VerifyPeerCertificateFunc
}

func columnSeparatedHex(d []byte) string {
h := strings.ToUpper(hex.EncodeToString(d))
var sb strings.Builder
for i, r := range h {
sb.WriteRune(r)
if i%2 == 1 && i != len(h)-1 {
sb.WriteRune(':')
}
}
return sb.String()
}

func certificateIdentifier(c *x509.Certificate) string {
return fmt.Sprintf(
"SN=%d, SKID=%s, AKID=%s",
c.SerialNumber,
columnSeparatedHex(c.SubjectKeyId),
columnSeparatedHex(c.AuthorityKeyId),
)
}

func (x *X509Authenticator) Authenticate(r *http.Request) (bool, string, error) {
if r.TLS == nil {
return false, "No TLS connection state in request", nil
}

if len(r.TLS.PeerCertificates) == 0 && x.requireClientCertsFn() {
return false, "A certificate is required to be sent by the client.", nil
}

var verifiedChains [][]*x509.Certificate
if len(r.TLS.PeerCertificates) > 0 && x.verifyOptionsFn != nil {
opts := x.verifyOptionsFn()
if opts.Intermediates == nil && len(r.TLS.PeerCertificates) > 1 {
opts.Intermediates = x509.NewCertPool()
for _, cert := range r.TLS.PeerCertificates[1:] {
opts.Intermediates.AddCert(cert)
}
}

chains, err := r.TLS.PeerCertificates[0].Verify(opts)
if err != nil {
return false, fmt.Sprintf("verifying certificate %s failed: %v", certificateIdentifier(r.TLS.PeerCertificates[0]), err), nil
}

verifiedChains = chains
}

if x.verifyPeerCertificateFn != nil {
rawCerts := make([][]byte, 0, len(r.TLS.PeerCertificates))
for _, c := range r.TLS.PeerCertificates {
rawCerts = append(rawCerts, c.Raw)
}

err := x.verifyPeerCertificateFn(rawCerts, verifiedChains)
if err != nil {
return false, fmt.Sprintf("verifying peer certificate failed: %v", err), nil
}
}

return true, "", nil
}

func NewX509Authenticator(requireClientCertsFn RequireClientCertsFunc, verifyOptionsFn VerifyOptionsFunc, verifyPeerCertificateFn VerifyPeerCertificateFunc) authentication.Authenticator {
return &X509Authenticator{
requireClientCertsFn: requireClientCertsFn,
verifyOptionsFn: verifyOptionsFn,
verifyPeerCertificateFn: verifyPeerCertificateFn,
}
}

var _ authentication.Authenticator = &X509Authenticator{}

// DefaultVerifyOptions returns VerifyOptions that use the system root certificates, current time,
// and requires certificates to be valid for client auth (x509.ExtKeyUsageClientAuth)
func DefaultVerifyOptions() x509.VerifyOptions {
return x509.VerifyOptions{
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
}
Loading

0 comments on commit ab8e6f5

Please sign in to comment.