@marcel91200 / caddy Mirror · read-only / commits / 0a14f97e
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 13, 16:45 UTC

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>

Gr33nbl00d committed Jun 2, 2022 at 22:25 UTC 0a14f97e4944a1aadc5fa9e6e8569759a9f0b5ec
1 file changed +82 -26
modules/caddytls/connpolicy.go
+82 -26
@@ -18,6 +18,7 @@ import (
18 "crypto/tls"
19 "crypto/x509"
20 "encoding/base64"
21 + "encoding/json"
22 "fmt"
23 "os"
24 "strings"
@@ -26,6 +27,10 @@ import (
27 "github.com/mholt/acmez"
28 )
29
30 +func init() {
31 + caddy.RegisterModule(LeafCertClientAuth{})
32 +}
33 +
34 // ConnectionPolicies govern the establishment of TLS connections. It is
35 // an ordered group of connection policies; the first matching policy will
36 // be used to configure TLS connections at handshake-time.
@@ -55,6 +60,16 @@ func (cp ConnectionPolicies) Provision(ctx caddy.Context) error {
60 if err != nil {
61 return fmt.Errorf("connection policy %d: building standard TLS config: %s", i, err)
62 }
63 +
64 + if pol.ClientAuthentication != nil && len(pol.ClientAuthentication.VerifiersRaw) > 0 {
65 + clientCertValidations, err := ctx.LoadModule(pol.ClientAuthentication, "VerifiersRaw")
66 + if err != nil {
67 + return fmt.Errorf("loading client cert verifiers: %v", err)
68 + }
69 + for _, validator := range clientCertValidations.([]interface{}) {
70 + cp[i].ClientAuthentication.verifiers = append(cp[i].ClientAuthentication.verifiers, validator.(ClientCertificateVerifier))
71 + }
72 + }
73 }
74
75 return nil
@@ -62,7 +77,7 @@ func (cp ConnectionPolicies) Provision(ctx caddy.Context) error {
77
78 // TLSConfig returns a standard-lib-compatible TLS configuration which
79 // selects the first matching policy based on the ClientHello.
65 -func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
80 +func (cp ConnectionPolicies) TLSConfig(_ caddy.Context) *tls.Config {
81 // using ServerName to match policies is extremely common, especially in configs
82 // with lots and lots of different policies; we can fast-track those by indexing
83 // them by SNI, so we don't have to iterate potentially thousands of policies
@@ -293,11 +308,22 @@ type ClientAuthentication struct {
308 // these CA certificates will be rejected.
309 TrustedCACertPEMFiles []string `json:"trusted_ca_certs_pem_files,omitempty"`
310
311 + // DEPRECATED: This field is deprecated and will be removed in
312 + // a future version. Please use the `validators` field instead
313 + // with the tls.client_auth.leaf module instead.
314 + //
315 // A list of base64 DER-encoded client leaf certs
316 // to accept. If this list is not empty, client certs
317 // which are not in this list will be rejected.
318 TrustedLeafCerts []string `json:"trusted_leaf_certs,omitempty"`
319
320 + // Client certificate verification modules. These can perform
321 + // custom client authentication checks, such as ensuring the
322 + // certificate is not revoked.
323 + VerifiersRaw []json.RawMessage `json:"verifiers,omitempty" caddy:"namespace=tls.client_auth inline_key=verifier"`
324 +
325 + verifiers []ClientCertificateVerifier
326 +
327 // The mode for authenticating the client. Allowed values are:
328 //
329 // Mode | Description
@@ -312,8 +338,6 @@ type ClientAuthentication struct {
338 // are provided; otherwise, the default mode is `require`.
339 Mode string `json:"mode,omitempty"`
340
315 - // state established with the last call to ConfigureTLSConfig
316 - trustedLeafCerts []*x509.Certificate
341 existingVerifyPeerCert func([][]byte, [][]*x509.Certificate) error
342 }
343
@@ -321,7 +345,8 @@ type ClientAuthentication struct {
345 func (clientauth ClientAuthentication) Active() bool {
346 return len(clientauth.TrustedCACerts) > 0 ||
347 len(clientauth.TrustedCACertPEMFiles) > 0 ||
324 - len(clientauth.TrustedLeafCerts) > 0 ||
348 + len(clientauth.TrustedLeafCerts) > 0 || // TODO: DEPRECATED
349 + len(clientauth.VerifiersRaw) > 0 ||
350 len(clientauth.Mode) > 0
351 }
352
@@ -378,28 +403,31 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
403 cfg.ClientCAs = caPool
404 }
405
381 - // enforce leaf verification by writing our own verify function
406 + // TODO: DEPRECATED: Only here for backwards compatibility.
407 + // If leaf cert is specified, enforce by adding a client auth module
408 if len(clientauth.TrustedLeafCerts) > 0 {
383 - clientauth.trustedLeafCerts = []*x509.Certificate{}
409 + caddy.Log().Named("tls.connection_policy").Warn("trusted_leaf_certs is deprecated; use leaf verifier module instead")
410 + var trustedLeafCerts []*x509.Certificate
411 for _, clientCertString := range clientauth.TrustedLeafCerts {
412 clientCert, err := decodeBase64DERCert(clientCertString)
413 if err != nil {
414 return fmt.Errorf("parsing certificate: %v", err)
415 }
389 - clientauth.trustedLeafCerts = append(clientauth.trustedLeafCerts, clientCert)
416 + trustedLeafCerts = append(trustedLeafCerts, clientCert)
417 }
391 - // if a custom verification function already exists, wrap it
392 - clientauth.existingVerifyPeerCert = cfg.VerifyPeerCertificate
393 - cfg.VerifyPeerCertificate = clientauth.verifyPeerCertificate
418 + clientauth.verifiers = append(clientauth.verifiers, LeafCertClientAuth{TrustedLeafCerts: trustedLeafCerts})
419 }
420
421 + // if a custom verification function already exists, wrap it
422 + clientauth.existingVerifyPeerCert = cfg.VerifyPeerCertificate
423 + cfg.VerifyPeerCertificate = clientauth.verifyPeerCertificate
424 return nil
425 }
426
427 // verifyPeerCertificate is for use as a tls.Config.VerifyPeerCertificate
428 // callback to do custom client certificate verification. It is intended
429 // for installation only by clientauth.ConfigureTLSConfig().
402 -func (clientauth ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
430 +func (clientauth *ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
431 // first use any pre-existing custom verification function
432 if clientauth.existingVerifyPeerCert != nil {
433 err := clientauth.existingVerifyPeerCert(rawCerts, verifiedChains)
@@ -407,23 +435,13 @@ func (clientauth ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte,
435 return err
436 }
437 }
410 -
411 - if len(rawCerts) == 0 {
412 - return fmt.Errorf("no client certificate provided")
413 - }
414 -
415 - remoteLeafCert, err := x509.ParseCertificate(rawCerts[0])
416 - if err != nil {
417 - return fmt.Errorf("can't parse the given certificate: %s", err.Error())
418 - }
419 -
420 - for _, trustedLeafCert := range clientauth.trustedLeafCerts {
421 - if remoteLeafCert.Equal(trustedLeafCert) {
422 - return nil
438 + for _, verifier := range clientauth.verifiers {
439 + err := verifier.VerifyClientCertificate(rawCerts, verifiedChains)
440 + if err != nil {
441 + return err
442 }
443 }
425 -
426 - return fmt.Errorf("client leaf certificate failed validation")
444 + return nil
445 }
446
447 // decodeBase64DERCert base64-decodes, then DER-decodes, certStr.
@@ -461,6 +479,38 @@ func setDefaultTLSParams(cfg *tls.Config) {
479 cfg.PreferServerCipherSuites = true
480 }
481
482 +// LeafCertClientAuth verifies the client's leaf certificate.
483 +type LeafCertClientAuth struct {
484 + TrustedLeafCerts []*x509.Certificate
485 +}
486 +
487 +// CaddyModule returns the Caddy module information.
488 +func (LeafCertClientAuth) CaddyModule() caddy.ModuleInfo {
489 + return caddy.ModuleInfo{
490 + ID: "tls.client_auth.leaf",
491 + New: func() caddy.Module { return new(LeafCertClientAuth) },
492 + }
493 +}
494 +
495 +func (l LeafCertClientAuth) VerifyClientCertificate(rawCerts [][]byte, _ [][]*x509.Certificate) error {
496 + if len(rawCerts) == 0 {
497 + return fmt.Errorf("no client certificate provided")
498 + }
499 +
500 + remoteLeafCert, err := x509.ParseCertificate(rawCerts[0])
501 + if err != nil {
502 + return fmt.Errorf("can't parse the given certificate: %s", err.Error())
503 + }
504 +
505 + for _, trustedLeafCert := range l.TrustedLeafCerts {
506 + if remoteLeafCert.Equal(trustedLeafCert) {
507 + return nil
508 + }
509 + }
510 +
511 + return fmt.Errorf("client leaf certificate failed validation")
512 +}
513 +
514 // PublicKeyAlgorithm is a JSON-unmarshalable wrapper type.
515 type PublicKeyAlgorithm x509.PublicKeyAlgorithm
516
@@ -481,4 +531,10 @@ type ConnectionMatcher interface {
531 Match(*tls.ClientHelloInfo) bool
532 }
533
534 +// ClientCertificateVerifier is a type which verifies client certificates.
535 +// It is called during verifyPeerCertificate in the TLS handshake.
536 +type ClientCertificateVerifier interface {
537 + VerifyClientCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
538 +}
539 +
540 var defaultALPN = []string{"h2", "http/1.1"}