summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Holt <mholt@users.noreply.github.com>2019-10-09 19:41:45 -0600
committerMatthew Holt <mholt@users.noreply.github.com>2019-10-09 19:41:45 -0600
commit85ce15a5ad0c0a0e24ed026b1f06a5a573c74283 (patch)
treebfebde7f17249a129256250d3535675e9204d54c
parentdedcfd4e3da1297cda2d09776e7c8135e0d960ce (diff)
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.
-rw-r--r--modules/caddytls/certselection.go71
1 files changed, 71 insertions, 0 deletions
diff --git a/modules/caddytls/certselection.go b/modules/caddytls/certselection.go
new file mode 100644
index 0000000..b56185a
--- /dev/null
+++ b/modules/caddytls/certselection.go
@@ -0,0 +1,71 @@
+package caddytls
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "math/big"
+
+ "github.com/caddyserver/caddy/v2"
+ "github.com/mholt/certmagic"
+)
+
+func init() {
+ caddy.RegisterModule(Policy{})
+}
+
+// Policy represents a policy for selecting the certificate used to
+// complete a handshake when there may be multiple options. All fields
+// specified must match the candidate certificate for it to be chosen.
+// This was needed to solve https://github.com/caddyserver/caddy/issues/2588.
+type Policy struct {
+ SerialNumber *big.Int `json:"serial_number,omitempty"`
+ SubjectOrganization string `json:"subject_organization,omitempty"`
+ PublicKeyAlgorithm PublicKeyAlgorithm `json:"public_key_algorithm,omitempty"`
+ Tag string `json:"tag,omitempty"`
+}
+
+// CaddyModule returns the Caddy module information.
+func (Policy) CaddyModule() caddy.ModuleInfo {
+ return caddy.ModuleInfo{
+ Name: "tls.certificate_selection.custom",
+ New: func() caddy.Module { return new(Policy) },
+ }
+}
+
+// SelectCertificate implements certmagic.CertificateSelector.
+func (p Policy) SelectCertificate(_ *tls.ClientHelloInfo, choices []certmagic.Certificate) (certmagic.Certificate, error) {
+ for _, cert := range choices {
+ if p.SerialNumber != nil && cert.SerialNumber.Cmp(p.SerialNumber) != 0 {
+ continue
+ }
+
+ if p.PublicKeyAlgorithm != PublicKeyAlgorithm(x509.UnknownPublicKeyAlgorithm) &&
+ PublicKeyAlgorithm(cert.PublicKeyAlgorithm) != p.PublicKeyAlgorithm {
+ continue
+ }
+
+ if p.SubjectOrganization != "" {
+ var matchOrg bool
+ for _, org := range cert.Subject.Organization {
+ if p.SubjectOrganization == org {
+ matchOrg = true
+ break
+ }
+ }
+ if !matchOrg {
+ continue
+ }
+ }
+
+ if p.Tag != "" && !cert.HasTag(p.Tag) {
+ continue
+ }
+
+ return cert, nil
+ }
+ return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy")
+}
+
+// Interface guard
+var _ certmagic.CertificateSelector = (*Policy)(nil)