summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)