From 9b4acc2449b10ff7c32eff4ddf1838cd2d93c9cf Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Tue, 1 Oct 2024 17:18:17 -0600 Subject: caddytls: Support new tls.context module (#6369) * caddytls: Support new tls.context module This allows modules to manipulate the context passed into CertMagic's GetCertificate function, which can be useful for tracing/metrics, or other custom logic. This is experimental and may resolve the request of a sponsor, so we'll see how it goes! * Derpy derp --- modules/caddytls/connpolicy.go | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'modules/caddytls/connpolicy.go') diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go index 2e2d4f748..f415fffa0 100644 --- a/modules/caddytls/connpolicy.go +++ b/modules/caddytls/connpolicy.go @@ -15,6 +15,7 @@ package caddytls import ( + "context" "crypto/tls" "crypto/x509" "encoding/base64" @@ -78,6 +79,14 @@ func (cp ConnectionPolicies) Provision(ctx caddy.Context) error { cp[i].ClientAuthentication.verifiers = append(cp[i].ClientAuthentication.verifiers, validator.(ClientCertificateVerifier)) } } + + if len(pol.HandshakeContextRaw) > 0 { + modIface, err := ctx.LoadModule(pol, "HandshakeContextRaw") + if err != nil { + return fmt.Errorf("loading handshake context module: %v", err) + } + cp[i].handshakeContext = modIface.(HandshakeContext) + } } return nil @@ -137,6 +146,7 @@ type ConnectionPolicy struct { // How to match this policy with a TLS ClientHello. If // this policy is the first to match, it will be used. MatchersRaw caddy.ModuleMap `json:"match,omitempty" caddy:"namespace=tls.handshake_match"` + matchers []ConnectionMatcher // How to choose a certificate if more than one matched // the given ServerName (SNI) value. @@ -192,6 +202,12 @@ type ConnectionPolicy struct { // This feature is EXPERIMENTAL and subject to change or removal. InsecureSecretsLog string `json:"insecure_secrets_log,omitempty"` + // A module that can manipulate the context passed into CertMagic's + // certificate management functions during TLS handshakes. + // EXPERIMENTAL - subject to change or removal. + HandshakeContextRaw json.RawMessage `json:"handshake_context,omitempty" caddy:"namespace=tls.context inline_key=module"` + handshakeContext HandshakeContext + // TLSConfig is the fully-formed, standard lib TLS config // used to serve TLS connections. Provision all // ConnectionPolicies to populate this. It is exported only @@ -199,8 +215,15 @@ type ConnectionPolicy struct { // if necessary (like to adjust NextProtos to disable HTTP/2), // and may be unexported in the future. TLSConfig *tls.Config `json:"-"` +} - matchers []ConnectionMatcher +type HandshakeContext interface { + // HandshakeContext returns a context to pass into CertMagic's + // GetCertificate function used to serve, load, and manage certs + // during TLS handshakes. Generally you'll start with the context + // from the ClientHelloInfo, but you may use other information + // from it as well. Return an error to abort the handshake. + HandshakeContext(*tls.ClientHelloInfo) (context.Context, error) } func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error { @@ -240,7 +263,18 @@ func (p *ConnectionPolicy) buildStandardTLSConfig(ctx caddy.Context) error { } cfg.DefaultServerName = p.DefaultSNI cfg.FallbackServerName = p.FallbackSNI - return cfg.GetCertificate(hello) + + // TODO: experimental: if a handshake context module is configured, allow it + // to modify the context before passing it into CertMagic's GetCertificate + ctx := hello.Context() + if p.handshakeContext != nil { + ctx, err = p.handshakeContext.HandshakeContext(hello) + if err != nil { + return nil, fmt.Errorf("handshake context: %v", err) + } + } + + return cfg.GetCertificateWithContext(ctx, hello) }, MinVersion: tls.VersionTLS12, MaxVersion: tls.VersionTLS13, -- cgit v1.2.3