summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/caddytls/certselection.go101
-rw-r--r--modules/caddytls/connpolicy.go19
2 files changed, 72 insertions, 48 deletions
diff --git a/modules/caddytls/certselection.go b/modules/caddytls/certselection.go
index 88b7e0b..8a24034 100644
--- a/modules/caddytls/certselection.go
+++ b/modules/caddytls/certselection.go
@@ -20,65 +20,104 @@ import (
"fmt"
"math/big"
- "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/certmagic"
)
-func init() {
- caddy.RegisterModule(CustomCertSelectionPolicy{})
-}
-
// CustomCertSelectionPolicy 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 CustomCertSelectionPolicy 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"`
-}
+ // The certificate must have one of these serial numbers.
+ SerialNumber []*big.Int `json:"serial_number,omitempty"`
-// CaddyModule returns the Caddy module information.
-func (CustomCertSelectionPolicy) CaddyModule() caddy.ModuleInfo {
- return caddy.ModuleInfo{
- ID: "tls.certificate_selection.custom",
- New: func() caddy.Module { return new(CustomCertSelectionPolicy) },
- }
+ // The certificate must have one of these organization names.
+ SubjectOrganization []string `json:"subject_organization,omitempty"`
+
+ // The certificate must use this public key algorithm.
+ PublicKeyAlgorithm PublicKeyAlgorithm `json:"public_key_algorithm,omitempty"`
+
+ // The certificate must have at least one of the tags in the list.
+ AnyTag []string `json:"any_tag,omitempty"`
+
+ // The certificate must have all of the tags in the list.
+ AllTags []string `json:"all_tags,omitempty"`
}
-// SelectCertificate implements certmagic.CertificateSelector.
-func (p CustomCertSelectionPolicy) SelectCertificate(_ *tls.ClientHelloInfo, choices []certmagic.Certificate) (certmagic.Certificate, error) {
+// SelectCertificate implements certmagic.CertificateSelector. It
+// only chooses a certificate that at least meets the criteria in
+// p. It then chooses the first non-expired certificate that is
+// compatible with the client. If none are valid, it chooses the
+// first viable candidate anyway.
+func (p CustomCertSelectionPolicy) SelectCertificate(hello *tls.ClientHelloInfo, choices []certmagic.Certificate) (certmagic.Certificate, error) {
+ viable := make([]certmagic.Certificate, 0, len(choices))
+
+nextChoice:
for _, cert := range choices {
- if p.SerialNumber != nil && cert.SerialNumber.Cmp(p.SerialNumber) != 0 {
- continue
+ if len(p.SerialNumber) > 0 {
+ var found bool
+ for _, sn := range p.SerialNumber {
+ if cert.Leaf.SerialNumber.Cmp(sn) == 0 {
+ found = true
+ break
+ }
+ }
+ if !found {
+ continue
+ }
+ }
+
+ if len(p.SubjectOrganization) > 0 {
+ var found bool
+ for _, subjOrg := range p.SubjectOrganization {
+ for _, org := range cert.Leaf.Subject.Organization {
+ if subjOrg == org {
+ found = true
+ break
+ }
+ }
+ }
+ if !found {
+ continue
+ }
}
if p.PublicKeyAlgorithm != PublicKeyAlgorithm(x509.UnknownPublicKeyAlgorithm) &&
- PublicKeyAlgorithm(cert.PublicKeyAlgorithm) != p.PublicKeyAlgorithm {
+ PublicKeyAlgorithm(cert.Leaf.PublicKeyAlgorithm) != p.PublicKeyAlgorithm {
continue
}
- if p.SubjectOrganization != "" {
- var matchOrg bool
- for _, org := range cert.Subject.Organization {
- if p.SubjectOrganization == org {
- matchOrg = true
+ if len(p.AnyTag) > 0 {
+ var found bool
+ for _, tag := range p.AnyTag {
+ if cert.HasTag(tag) {
+ found = true
break
}
}
- if !matchOrg {
+ if !found {
continue
}
}
- if p.Tag != "" && !cert.HasTag(p.Tag) {
- continue
+ if len(p.AllTags) > 0 {
+ for _, tag := range p.AllTags {
+ if !cert.HasTag(tag) {
+ continue nextChoice
+ }
+ }
}
- return cert, nil
+ // this certificate at least meets the policy's requirements,
+ // but we still have to check expiration and compatibility
+ viable = append(viable, cert)
}
- return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy")
+
+ if len(viable) == 0 {
+ return certmagic.Certificate{}, fmt.Errorf("no certificates matched custom selection policy")
+ }
+
+ return certmagic.DefaultCertificateSelector(hello, viable)
}
// Interface guard
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index 52ccdd9..4fd8112 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -18,12 +18,10 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
- "encoding/json"
"fmt"
"strings"
"github.com/caddyserver/caddy/v2"
- "github.com/caddyserver/certmagic"
"github.com/go-acme/lego/v3/challenge/tlsalpn01"
)
@@ -46,15 +44,6 @@ func (cp ConnectionPolicies) Provision(ctx caddy.Context) error {
cp[i].matchers = append(cp[i].matchers, modIface.(ConnectionMatcher))
}
- // certificate selector
- if pol.CertSelection != nil {
- val, err := ctx.LoadModule(pol, "CertSelection")
- if err != nil {
- return fmt.Errorf("loading certificate selection module: %s", err)
- }
- cp[i].certSelector = val.(certmagic.CertificateSelector)
- }
-
// enable HTTP/2 by default
if len(pol.ALPN) == 0 {
pol.ALPN = append(pol.ALPN, defaultALPN...)
@@ -123,7 +112,7 @@ type ConnectionPolicy struct {
// How to choose a certificate if more than one matched
// the given ServerName (SNI) value.
- CertSelection json.RawMessage `json:"certificate_selection,omitempty" caddy:"namespace=tls.certificate_selection inline_key=policy"`
+ CertSelection *CustomCertSelectionPolicy `json:"certificate_selection,omitempty"`
// The list of cipher suites to support. Caddy's
// defaults are modern and secure.
@@ -151,8 +140,6 @@ type ConnectionPolicy struct {
DefaultSNI string `json:"default_sni,omitempty"`
matchers []ConnectionMatcher
- certSelector certmagic.CertificateSelector
-
stdTLSConfig *tls.Config
}
@@ -184,9 +171,7 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error {
// more at handshake-time, but I don't know how to practically pre-build
// a certmagic config for each combination of conn policy + automation policy...
cfg := *tlsApp.getConfigForName(hello.ServerName)
- if p.certSelector != nil {
- cfg.CertSelection = p.certSelector
- }
+ cfg.CertSelection = p.CertSelection
cfg.DefaultServerName = p.DefaultSNI
return cfg.GetCertificate(hello)
},