mirror of
https://github.com/caddyserver/caddy.git
synced 2026-04-23 17:39:33 -04:00
tls: centralise HTTPS RR ALPN defaults and registration
Reuse shared protocol defaults instead of repeating the default HTTP protocol list, unify server name registration to carry ALPN in one experimental API and reuse the TLS default ALPN ordering for HTTPS RR publication
This commit is contained in:
parent
aacb430d87
commit
904f9fddcc
@ -23,6 +23,7 @@ import (
|
||||
"maps"
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@ -235,7 +236,7 @@ func (app *App) Provision(ctx caddy.Context) error {
|
||||
|
||||
// if no protocols configured explicitly, enable all except h2c
|
||||
if len(srv.Protocols) == 0 {
|
||||
srv.Protocols = []string{"h1", "h2", "h3"}
|
||||
srv.Protocols = slices.Clone(srv.protocolsWithDefaults())
|
||||
}
|
||||
|
||||
srvProtocolsUnique := map[string]struct{}{}
|
||||
|
||||
@ -173,7 +173,7 @@ func (app *App) automaticHTTPSPhase1(ctx caddy.Context, repl *caddy.Replacer) er
|
||||
for d := range serverDomainSet {
|
||||
echDomains = append(echDomains, d)
|
||||
}
|
||||
app.tlsApp.RegisterServerNamesWithALPN(echDomains, httpsRRALPNs(srv))
|
||||
app.tlsApp.RegisterServerNames(echDomains, httpsRRALPNs(srv))
|
||||
|
||||
// nothing more to do here if there are no domains that qualify for
|
||||
// automatic HTTPS and there are no explicit TLS connection policies:
|
||||
@ -553,10 +553,7 @@ func (app *App) makeRedirRoute(redirToPort uint, matcherSet MatcherSet) Route {
|
||||
func httpsRRALPNs(srv *Server) []string {
|
||||
// Automatic HTTPS runs before server provisioning fills in the default
|
||||
// protocols, so derive the effective set directly from the raw config here.
|
||||
serverProtocols := srv.Protocols
|
||||
if len(serverProtocols) == 0 {
|
||||
serverProtocols = []string{"h1", "h2", "h3"}
|
||||
}
|
||||
serverProtocols := srv.protocolsWithDefaults()
|
||||
|
||||
protocols := make(map[string]struct{}, len(serverProtocols))
|
||||
if srv.ListenProtocols == nil {
|
||||
@ -583,17 +580,17 @@ func httpsRRALPNs(srv *Server) []string {
|
||||
}
|
||||
}
|
||||
|
||||
alpn := make([]string, 0, 3)
|
||||
alpn := make(map[string]struct{}, 3)
|
||||
if _, ok := protocols["h3"]; ok {
|
||||
alpn = append(alpn, "h3")
|
||||
alpn["h3"] = struct{}{}
|
||||
}
|
||||
if _, ok := protocols["h2"]; ok {
|
||||
alpn = append(alpn, "h2")
|
||||
alpn["h2"] = struct{}{}
|
||||
}
|
||||
if _, ok := protocols["h1"]; ok {
|
||||
alpn = append(alpn, "http/1.1")
|
||||
alpn["http/1.1"] = struct{}{}
|
||||
}
|
||||
return alpn
|
||||
return caddytls.OrderedHTTPSRRALPN(alpn)
|
||||
}
|
||||
|
||||
// createAutomationPolicies ensures that automated certificates for this
|
||||
|
||||
@ -301,6 +301,8 @@ type Server struct {
|
||||
onStopFuncs []func(context.Context) error // TODO: Experimental (Nov. 2023)
|
||||
}
|
||||
|
||||
var defaultProtocols = []string{"h1", "h2", "h3"}
|
||||
|
||||
var (
|
||||
ServerHeader = "Caddy"
|
||||
serverHeader = []string{ServerHeader}
|
||||
@ -900,13 +902,14 @@ func (s *Server) logRequest(
|
||||
// protocol returns true if the protocol proto is configured/enabled.
|
||||
func (s *Server) protocol(proto string) bool {
|
||||
if s.ListenProtocols == nil {
|
||||
if slices.Contains(s.Protocols, proto) {
|
||||
if slices.Contains(s.protocolsWithDefaults(), proto) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
serverProtocols := s.protocolsWithDefaults()
|
||||
for _, lnProtocols := range s.ListenProtocols {
|
||||
for _, lnProtocol := range lnProtocols {
|
||||
if lnProtocol == "" && slices.Contains(s.Protocols, proto) || lnProtocol == proto {
|
||||
if lnProtocol == "" && slices.Contains(serverProtocols, proto) || lnProtocol == proto {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -916,6 +919,13 @@ func (s *Server) protocol(proto string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) protocolsWithDefaults() []string {
|
||||
if len(s.Protocols) == 0 {
|
||||
return defaultProtocols
|
||||
}
|
||||
return s.Protocols
|
||||
}
|
||||
|
||||
// Listeners returns the server's listeners. These are active listeners,
|
||||
// so calling Accept() or Close() on them will probably break things.
|
||||
// They are made available here for read-only purposes (e.g. Addr())
|
||||
|
||||
@ -153,9 +153,9 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
|
||||
// in its config (remember, TLS connection policies are used by *other* apps to
|
||||
// run TLS servers) -- we skip names with placeholders
|
||||
if tlsApp.EncryptedClientHello.Publication == nil {
|
||||
var echNames []string
|
||||
repl := caddy.NewReplacer()
|
||||
for _, p := range cp {
|
||||
var echNames []string
|
||||
for _, m := range p.matchers {
|
||||
if sni, ok := m.(MatchServerName); ok {
|
||||
for _, name := range sni {
|
||||
@ -164,8 +164,8 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
|
||||
}
|
||||
}
|
||||
}
|
||||
tlsApp.RegisterServerNames(echNames, p.ALPN)
|
||||
}
|
||||
tlsApp.RegisterServerNames(echNames)
|
||||
}
|
||||
|
||||
tlsCfg.GetEncryptedClientHelloKeys = func(chi *tls.ClientHelloInfo) ([]tls.EncryptedClientHelloKey, error) {
|
||||
|
||||
@ -11,17 +11,16 @@ import (
|
||||
|
||||
func TestRegisterServerNamesWithALPN(t *testing.T) {
|
||||
tlsApp := &TLS{
|
||||
serverNames: make(map[string]struct{}),
|
||||
serverNameALPN: make(map[string]map[string]struct{}),
|
||||
serverNamesMu: new(sync.Mutex),
|
||||
serverNames: make(map[string]serverNameRegistration),
|
||||
serverNamesMu: new(sync.Mutex),
|
||||
}
|
||||
|
||||
tlsApp.RegisterServerNamesWithALPN([]string{
|
||||
tlsApp.RegisterServerNames([]string{
|
||||
"Example.com:443",
|
||||
"example.com",
|
||||
"127.0.0.1:443",
|
||||
}, []string{"h2", "http/1.1"})
|
||||
tlsApp.RegisterServerNamesWithALPN([]string{"EXAMPLE.COM"}, []string{"h3"})
|
||||
tlsApp.RegisterServerNames([]string{"EXAMPLE.COM"}, []string{"h3"})
|
||||
|
||||
got := tlsApp.alpnValuesForServerNames([]string{"example.com:443", "127.0.0.1:443"})
|
||||
want := map[string][]string{
|
||||
|
||||
@ -141,9 +141,8 @@ type TLS struct {
|
||||
logger *zap.Logger
|
||||
events *caddyevents.App
|
||||
|
||||
serverNames map[string]struct{}
|
||||
serverNameALPN map[string]map[string]struct{}
|
||||
serverNamesMu *sync.Mutex
|
||||
serverNames map[string]serverNameRegistration
|
||||
serverNamesMu *sync.Mutex
|
||||
|
||||
// set of subjects with managed certificates,
|
||||
// and hashes of manually-loaded certificates
|
||||
@ -170,8 +169,7 @@ func (t *TLS) Provision(ctx caddy.Context) error {
|
||||
t.logger = ctx.Logger()
|
||||
repl := caddy.NewReplacer()
|
||||
t.managing, t.loaded = make(map[string]string), make(map[string]string)
|
||||
t.serverNames = make(map[string]struct{})
|
||||
t.serverNameALPN = make(map[string]map[string]struct{})
|
||||
t.serverNames = make(map[string]serverNameRegistration)
|
||||
t.serverNamesMu = new(sync.Mutex)
|
||||
|
||||
// set up default DNS module, if any, and make sure it implements all the
|
||||
@ -651,24 +649,16 @@ func (t *TLS) managingWildcardFor(subj string, otherSubjsToManage map[string]str
|
||||
return false
|
||||
}
|
||||
|
||||
// RegisterServerNames registers the provided DNS names with the TLS app.
|
||||
// This is currently used to auto-publish Encrypted ClientHello (ECH)
|
||||
// configurations, if enabled. Use of this function by apps using the TLS
|
||||
// app removes the need for the user to redundantly specify domain names
|
||||
// in their configuration. This function separates hostname and port
|
||||
// (keeping only the hotsname) and filters IP addresses, which can't be
|
||||
// used with ECH.
|
||||
// RegisterServerNames registers the provided DNS names with the TLS app and
|
||||
// associates them with the given HTTPS RR ALPN values, if any. This is
|
||||
// currently used to auto-publish Encrypted ClientHello (ECH) configurations,
|
||||
// if enabled. Use of this function by apps using the TLS app removes the need
|
||||
// for the user to redundantly specify domain names in their configuration.
|
||||
// This function separates hostname and port, keeping only the hostname, and
|
||||
// filters IP addresses which can't be used with ECH.
|
||||
//
|
||||
// EXPERIMENTAL: This function and its semantics/behavior are subject to change.
|
||||
func (t *TLS) RegisterServerNames(dnsNames []string) {
|
||||
t.RegisterServerNamesWithALPN(dnsNames, nil)
|
||||
}
|
||||
|
||||
// RegisterServerNamesWithALPN registers the provided DNS names with the TLS app
|
||||
// and associates them with the given HTTPS RR ALPN values, if any.
|
||||
//
|
||||
// EXPERIMENTAL: This function and its semantics/behavior are subject to change.
|
||||
func (t *TLS) RegisterServerNamesWithALPN(dnsNames []string, alpnValues []string) {
|
||||
func (t *TLS) RegisterServerNames(dnsNames, alpnValues []string) {
|
||||
t.serverNamesMu.Lock()
|
||||
defer t.serverNamesMu.Unlock()
|
||||
|
||||
@ -681,21 +671,24 @@ func (t *TLS) RegisterServerNamesWithALPN(dnsNames []string, alpnValues []string
|
||||
if host == "" || certmagic.SubjectIsIP(host) {
|
||||
continue
|
||||
}
|
||||
t.serverNames[host] = struct{}{}
|
||||
|
||||
registration := t.serverNames[host]
|
||||
|
||||
if len(alpnValues) == 0 {
|
||||
t.serverNames[host] = registration
|
||||
continue
|
||||
}
|
||||
|
||||
if t.serverNameALPN[host] == nil {
|
||||
t.serverNameALPN[host] = make(map[string]struct{}, len(alpnValues))
|
||||
if registration.alpnValues == nil {
|
||||
registration.alpnValues = make(map[string]struct{}, len(alpnValues))
|
||||
}
|
||||
for _, alpn := range alpnValues {
|
||||
if alpn == "" {
|
||||
continue
|
||||
}
|
||||
t.serverNameALPN[host][alpn] = struct{}{}
|
||||
registration.alpnValues[alpn] = struct{}{}
|
||||
}
|
||||
t.serverNames[host] = registration
|
||||
}
|
||||
}
|
||||
|
||||
@ -714,22 +707,23 @@ func (t *TLS) alpnValuesForServerNames(dnsNames []string) map[string][]string {
|
||||
continue
|
||||
}
|
||||
|
||||
alpnSet := t.serverNameALPN[host]
|
||||
if len(alpnSet) == 0 {
|
||||
registration, ok := t.serverNames[host]
|
||||
if !ok || len(registration.alpnValues) == 0 {
|
||||
continue
|
||||
}
|
||||
result[host] = orderedHTTPSRRALPN(alpnSet)
|
||||
result[host] = OrderedHTTPSRRALPN(registration.alpnValues)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func orderedHTTPSRRALPN(alpnSet map[string]struct{}) []string {
|
||||
// OrderedHTTPSRRALPN returns the HTTPS RR ALPN values in preferred order.
|
||||
func OrderedHTTPSRRALPN(alpnSet map[string]struct{}) []string {
|
||||
if len(alpnSet) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
knownOrder := []string{"h3", "h2", "http/1.1"}
|
||||
knownOrder := append([]string{"h3"}, defaultALPN...)
|
||||
ordered := make([]string, 0, len(alpnSet))
|
||||
seen := make(map[string]struct{}, len(alpnSet))
|
||||
|
||||
@ -756,6 +750,10 @@ func orderedHTTPSRRALPN(alpnSet map[string]struct{}) []string {
|
||||
return append(ordered, remaining...)
|
||||
}
|
||||
|
||||
type serverNameRegistration struct {
|
||||
alpnValues map[string]struct{}
|
||||
}
|
||||
|
||||
// HandleHTTPChallenge ensures that the ACME HTTP challenge or ZeroSSL HTTP
|
||||
// validation request is handled for the certificate named by r.Host, if it
|
||||
// is an HTTP challenge request. It requires that the automation policy for
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user