@marcel91200 / caddy Mirror · read-only / commits / 85ce15a5
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 14, 05:15 UTC

tls: Add custom certificate selection policy

This migrates a feature that was previously reserved for enterprise users, according to https://github.com/caddyserver/caddy/issues/2786. Custom certificate selection policies allow advanced control over which cert is selected when multiple qualify to satisfy a TLS handshake.

Matthew Holt committed Oct 9, 2019 at 19:41 UTC 85ce15a5ad0c0a0e24ed026b1f06a5a573c74283
1 file changed +71
modules/caddytls/certselection.go new
+71
@@ -0,0 +1,71 @@
1 +package caddytls
2 +
3 +import (
4 + "crypto/tls"
5 + "crypto/x509"
6 + "fmt"
7 + "math/big"
8 +
9 + "github.com/caddyserver/caddy/v2"
10 + "github.com/mholt/certmagic"
11 +)
12 +
13 +func init() {
14 + caddy.RegisterModule(Policy{})
15 +}
16 +
17 +// Policy represents a policy for selecting the certificate used to
18 +// complete a handshake when there may be multiple options. All fields
19 +// specified must match the candidate certificate for it to be chosen.
20 +// This was needed to solve https://github.com/caddyserver/caddy/issues/2588.
21 +type Policy struct {
22 + SerialNumber *big.Int `json:"serial_number,omitempty"`
23 + SubjectOrganization string `json:"subject_organization,omitempty"`
24 + PublicKeyAlgorithm PublicKeyAlgorithm `json:"public_key_algorithm,omitempty"`
25 + Tag string `json:"tag,omitempty"`
26 +}
27 +
28 +// CaddyModule returns the Caddy module information.
29 +func (Policy) CaddyModule() caddy.ModuleInfo {
30 + return caddy.ModuleInfo{
31 + Name: "tls.certificate_selection.custom",
32 + New: func() caddy.Module { return new(Policy) },
33 + }
34 +}
35 +
36 +// SelectCertificate implements certmagic.CertificateSelector.
37 +func (p Policy) SelectCertificate(_ *tls.ClientHelloInfo, choices []certmagic.Certificate) (certmagic.Certificate, error) {
38 + for _, cert := range choices {
39 + if p.SerialNumber != nil && cert.SerialNumber.Cmp(p.SerialNumber) != 0 {
40 + continue
41 + }
42 +
43 + if p.PublicKeyAlgorithm != PublicKeyAlgorithm(x509.UnknownPublicKeyAlgorithm) &&
44 + PublicKeyAlgorithm(cert.PublicKeyAlgorithm) != p.PublicKeyAlgorithm {
45 + continue
46 + }
47 +
48 + if p.SubjectOrganization != "" {
49 + var matchOrg bool
50 + for _, org := range cert.Subject.Organization {
51 + if p.SubjectOrganization == org {
52 + matchOrg = true
53 + break
54 + }
55 + }
56 + if !matchOrg {
57 + continue
58 + }
59 + }
60 +
61 + if p.Tag != "" && !cert.HasTag(p.Tag) {
62 + continue
63 + }
64 +
65 + return cert, nil
66 + }
67 + return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy")
68 +}
69 +
70 +// Interface guard
71 +var _ certmagic.CertificateSelector = (*Policy)(nil)