summaryrefslogtreecommitdiff
path: root/modules/caddytls
diff options
context:
space:
mode:
authorGr33nbl00d <florian.roettges@googlemail.com>2022-06-02 22:25:07 +0200
committerGitHub <noreply@github.com>2022-06-02 14:25:07 -0600
commit0a14f97e4944a1aadc5fa9e6e8569759a9f0b5ec (patch)
tree3561fe9d1e0e72c35ee5d5f1e25ad429378d3b0d /modules/caddytls
parent9864b138fbae57615998a27158787f4468247476 (diff)
caddytls: Make peer certificate verification pluggable (#4389)
* caddytls: Adding ClientCertValidator for custom client cert validations * caddytls: Cleanups for ClientCertValidator changes caddytls: Cleanups for ClientCertValidator changes * Update modules/caddytls/connpolicy.go Co-authored-by: Francis Lavoie <lavofr@gmail.com> * Update modules/caddytls/connpolicy.go Co-authored-by: Francis Lavoie <lavofr@gmail.com> * Update modules/caddytls/connpolicy.go Co-authored-by: Francis Lavoie <lavofr@gmail.com> * Update modules/caddytls/connpolicy.go Co-authored-by: Francis Lavoie <lavofr@gmail.com> * Update modules/caddytls/connpolicy.go Co-authored-by: Matt Holt <mholt@users.noreply.github.com> * Update modules/caddytls/connpolicy.go Co-authored-by: Matt Holt <mholt@users.noreply.github.com> * Unexported field Validators, corrected renaming of LeafVerificationValidator to LeafCertClientAuth * admin: Write proper status on invalid requests (#4569) (fix #4561) * Apply suggestions from code review * Register module; fix compilation * Add log for deprecation notice Co-authored-by: Roettges Florian <roettges.florian@scheidt-bachmann.de> Co-authored-by: Francis Lavoie <lavofr@gmail.com> Co-authored-by: Matt Holt <mholt@users.noreply.github.com> Co-authored-by: Alok Naushad <alokme123@gmail.com>
Diffstat (limited to 'modules/caddytls')
-rw-r--r--modules/caddytls/connpolicy.go108
1 files changed, 82 insertions, 26 deletions
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index 65cda5f..d6304a9 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -18,6 +18,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
+ "encoding/json"
"fmt"
"os"
"strings"
@@ -26,6 +27,10 @@ import (
"github.com/mholt/acmez"
)
+func init() {
+ caddy.RegisterModule(LeafCertClientAuth{})
+}
+
// ConnectionPolicies govern the establishment of TLS connections. It is
// an ordered group of connection policies; the first matching policy will
// be used to configure TLS connections at handshake-time.
@@ -55,6 +60,16 @@ func (cp ConnectionPolicies) Provision(ctx caddy.Context) error {
if err != nil {
return fmt.Errorf("connection policy %d: building standard TLS config: %s", i, err)
}
+
+ if pol.ClientAuthentication != nil && len(pol.ClientAuthentication.VerifiersRaw) > 0 {
+ clientCertValidations, err := ctx.LoadModule(pol.ClientAuthentication, "VerifiersRaw")
+ if err != nil {
+ return fmt.Errorf("loading client cert verifiers: %v", err)
+ }
+ for _, validator := range clientCertValidations.([]interface{}) {
+ cp[i].ClientAuthentication.verifiers = append(cp[i].ClientAuthentication.verifiers, validator.(ClientCertificateVerifier))
+ }
+ }
}
return nil
@@ -62,7 +77,7 @@ func (cp ConnectionPolicies) Provision(ctx caddy.Context) error {
// TLSConfig returns a standard-lib-compatible TLS configuration which
// selects the first matching policy based on the ClientHello.
-func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
+func (cp ConnectionPolicies) TLSConfig(_ caddy.Context) *tls.Config {
// using ServerName to match policies is extremely common, especially in configs
// with lots and lots of different policies; we can fast-track those by indexing
// them by SNI, so we don't have to iterate potentially thousands of policies
@@ -293,11 +308,22 @@ type ClientAuthentication struct {
// these CA certificates will be rejected.
TrustedCACertPEMFiles []string `json:"trusted_ca_certs_pem_files,omitempty"`
+ // DEPRECATED: This field is deprecated and will be removed in
+ // a future version. Please use the `validators` field instead
+ // with the tls.client_auth.leaf module instead.
+ //
// A list of base64 DER-encoded client leaf certs
// to accept. If this list is not empty, client certs
// which are not in this list will be rejected.
TrustedLeafCerts []string `json:"trusted_leaf_certs,omitempty"`
+ // Client certificate verification modules. These can perform
+ // custom client authentication checks, such as ensuring the
+ // certificate is not revoked.
+ VerifiersRaw []json.RawMessage `json:"verifiers,omitempty" caddy:"namespace=tls.client_auth inline_key=verifier"`
+
+ verifiers []ClientCertificateVerifier
+
// The mode for authenticating the client. Allowed values are:
//
// Mode | Description
@@ -312,8 +338,6 @@ type ClientAuthentication struct {
// are provided; otherwise, the default mode is `require`.
Mode string `json:"mode,omitempty"`
- // state established with the last call to ConfigureTLSConfig
- trustedLeafCerts []*x509.Certificate
existingVerifyPeerCert func([][]byte, [][]*x509.Certificate) error
}
@@ -321,7 +345,8 @@ type ClientAuthentication struct {
func (clientauth ClientAuthentication) Active() bool {
return len(clientauth.TrustedCACerts) > 0 ||
len(clientauth.TrustedCACertPEMFiles) > 0 ||
- len(clientauth.TrustedLeafCerts) > 0 ||
+ len(clientauth.TrustedLeafCerts) > 0 || // TODO: DEPRECATED
+ len(clientauth.VerifiersRaw) > 0 ||
len(clientauth.Mode) > 0
}
@@ -378,28 +403,31 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
cfg.ClientCAs = caPool
}
- // enforce leaf verification by writing our own verify function
+ // TODO: DEPRECATED: Only here for backwards compatibility.
+ // If leaf cert is specified, enforce by adding a client auth module
if len(clientauth.TrustedLeafCerts) > 0 {
- clientauth.trustedLeafCerts = []*x509.Certificate{}
+ caddy.Log().Named("tls.connection_policy").Warn("trusted_leaf_certs is deprecated; use leaf verifier module instead")
+ var trustedLeafCerts []*x509.Certificate
for _, clientCertString := range clientauth.TrustedLeafCerts {
clientCert, err := decodeBase64DERCert(clientCertString)
if err != nil {
return fmt.Errorf("parsing certificate: %v", err)
}
- clientauth.trustedLeafCerts = append(clientauth.trustedLeafCerts, clientCert)
+ trustedLeafCerts = append(trustedLeafCerts, clientCert)
}
- // if a custom verification function already exists, wrap it
- clientauth.existingVerifyPeerCert = cfg.VerifyPeerCertificate
- cfg.VerifyPeerCertificate = clientauth.verifyPeerCertificate
+ clientauth.verifiers = append(clientauth.verifiers, LeafCertClientAuth{TrustedLeafCerts: trustedLeafCerts})
}
+ // if a custom verification function already exists, wrap it
+ clientauth.existingVerifyPeerCert = cfg.VerifyPeerCertificate
+ cfg.VerifyPeerCertificate = clientauth.verifyPeerCertificate
return nil
}
// verifyPeerCertificate is for use as a tls.Config.VerifyPeerCertificate
// callback to do custom client certificate verification. It is intended
// for installation only by clientauth.ConfigureTLSConfig().
-func (clientauth ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
+func (clientauth *ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// first use any pre-existing custom verification function
if clientauth.existingVerifyPeerCert != nil {
err := clientauth.existingVerifyPeerCert(rawCerts, verifiedChains)
@@ -407,23 +435,13 @@ func (clientauth ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte,
return err
}
}
-
- if len(rawCerts) == 0 {
- return fmt.Errorf("no client certificate provided")
- }
-
- remoteLeafCert, err := x509.ParseCertificate(rawCerts[0])
- if err != nil {
- return fmt.Errorf("can't parse the given certificate: %s", err.Error())
- }
-
- for _, trustedLeafCert := range clientauth.trustedLeafCerts {
- if remoteLeafCert.Equal(trustedLeafCert) {
- return nil
+ for _, verifier := range clientauth.verifiers {
+ err := verifier.VerifyClientCertificate(rawCerts, verifiedChains)
+ if err != nil {
+ return err
}
}
-
- return fmt.Errorf("client leaf certificate failed validation")
+ return nil
}
// decodeBase64DERCert base64-decodes, then DER-decodes, certStr.
@@ -461,6 +479,38 @@ func setDefaultTLSParams(cfg *tls.Config) {
cfg.PreferServerCipherSuites = true
}
+// LeafCertClientAuth verifies the client's leaf certificate.
+type LeafCertClientAuth struct {
+ TrustedLeafCerts []*x509.Certificate
+}
+
+// CaddyModule returns the Caddy module information.
+func (LeafCertClientAuth) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ ID: "tls.client_auth.leaf",
+ New: func() caddy.Module { return new(LeafCertClientAuth) },
+ }
+}
+
+func (l LeafCertClientAuth) VerifyClientCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
+ if len(rawCerts) == 0 {
+ return fmt.Errorf("no client certificate provided")
+ }
+
+ remoteLeafCert, err := x509.ParseCertificate(rawCerts[0])
+ if err != nil {
+ return fmt.Errorf("can't parse the given certificate: %s", err.Error())
+ }
+
+ for _, trustedLeafCert := range l.TrustedLeafCerts {
+ if remoteLeafCert.Equal(trustedLeafCert) {
+ return nil
+ }
+ }
+
+ return fmt.Errorf("client leaf certificate failed validation")
+}
+
// PublicKeyAlgorithm is a JSON-unmarshalable wrapper type.
type PublicKeyAlgorithm x509.PublicKeyAlgorithm
@@ -481,4 +531,10 @@ type ConnectionMatcher interface {
Match(*tls.ClientHelloInfo) bool
}
+// ClientCertificateVerifier is a type which verifies client certificates.
+// It is called during verifyPeerCertificate in the TLS handshake.
+type ClientCertificateVerifier interface {
+ VerifyClientCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+}
+
var defaultALPN = []string{"h2", "http/1.1"}