18
"crypto/tls"
19
"crypto/x509"
20
"encoding/base64"
21
+ "encoding/json"
22
"fmt"
23
"os"
24
"strings"
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.
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
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
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
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
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
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)
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.
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
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"}