Implement SendAsRetry

This commit is contained in:
Matthew Holt 2025-03-05 16:53:51 -07:00
parent 6dff446a91
commit 8835568c8a
No known key found for this signature in database
GPG Key ID: 2A349DD577D586A5
2 changed files with 30 additions and 14 deletions

View File

@ -169,13 +169,14 @@ func (cp ConnectionPolicies) TLSConfig(ctx caddy.Context) *tls.Config {
// TODO: Ideally, ECH keys should be rotated. However, as of Go 1.24, the std lib implementation
// does not support safely modifying the tls.Config's EncryptedClientHelloKeys field.
// So, we implement static ECH keys temporarily. See https://github.com/golang/go/issues/71920.
// Revisit this after Go 1.25 is released and implement key rotation.
var stdECHKeys []tls.EncryptedClientHelloKey
for _, echConfigs := range tlsApp.EncryptedClientHello.configs {
for _, c := range echConfigs {
stdECHKeys = append(stdECHKeys, tls.EncryptedClientHelloKey{
Config: c.configBin,
PrivateKey: c.privKeyBin,
// TODO: SendAsRetry: true if the config is current (explicitly configured and most recent)
Config: c.configBin,
PrivateKey: c.privKeyBin,
SendAsRetry: c.sendAsRetry,
})
}
}

View File

@ -112,13 +112,26 @@ func (ech *ECH) Provision(ctx caddy.Context) ([]string, error) {
}
// all existing configs are now loaded; see if we need to make any new ones
// based on the input configuration
// based on the input configuration, and also mark the most recent one(s) as
// current/active, so they can be used for ECH retries
for _, cfg := range ech.Configs {
publicName := strings.ToLower(strings.TrimSpace(cfg.OuterSNI))
// if no config is loaded for this public name, we need to create one
if list, ok := ech.configs[publicName]; !ok || len(list) == 0 {
if list, ok := ech.configs[publicName]; ok && len(list) > 0 {
// at least one config with this public name was loaded, so find the
// most recent one and mark it as active to be used with retries
var mostRecentDate time.Time
var mostRecentIdx int
for i, c := range list {
if mostRecentDate.IsZero() || c.meta.Created.After(mostRecentDate) {
mostRecentDate = c.meta.Created
mostRecentIdx = i
}
}
list[mostRecentIdx].sendAsRetry = true
} else {
// no config with this public name was loaded, so create one
echCfg, err := generateAndStoreECHConfig(ctx, publicName)
if err != nil {
return nil, err
@ -211,14 +224,14 @@ func (t *TLS) publishECHConfigs() error {
// by default, publish for all (non-outer) server names, unless
// a specific list of names is configured
var serverNamesSet map[string]struct{}
if publication.DNSNames == nil {
if publication.Domains == nil {
serverNamesSet = make(map[string]struct{}, len(t.serverNames))
for name := range t.serverNames {
serverNamesSet[name] = struct{}{}
}
} else {
serverNamesSet = make(map[string]struct{}, len(publication.DNSNames))
for _, name := range publication.DNSNames {
serverNamesSet = make(map[string]struct{}, len(publication.Domains))
for _, name := range publication.Domains {
serverNamesSet[name] = struct{}{}
}
}
@ -263,7 +276,7 @@ func (t *TLS) publishECHConfigs() error {
err := publisher.PublishECHConfigList(t.ctx, dnsNamesToPublish, echCfgListBin)
if err != nil {
t.logger.Error("publishing ECH configuration list",
zap.Strings("for_dns_names", publication.DNSNames),
zap.Strings("for_domains", publication.Domains),
zap.Error(err))
}
@ -424,6 +437,7 @@ func generateAndStoreECHConfig(ctx caddy.Context, publicName string) (echConfig,
AEADID: hpke.AEAD_ChaCha20Poly1305,
},
},
sendAsRetry: true,
}
meta := echConfigMeta{
Created: time.Now(),
@ -510,7 +524,7 @@ type ECHPublication struct {
// purpose of ECH. Hence the need to list them here so Caddy can
// proactively publish ECH configs before clients connect with those
// server names in plaintext.
DNSNames []string `json:"dns_names,omitempty"`
Domains []string `json:"domains,omitempty"`
// How to publish the ECH configurations so clients can know to use them.
// Note that ECH configs are only published when they are newly created,
@ -663,9 +677,10 @@ type echConfig struct {
// these fields are not part of the spec, but are here for
// our use when setting up TLS servers or maintenance
configBin []byte
privKeyBin []byte
meta echConfigMeta
configBin []byte
privKeyBin []byte
meta echConfigMeta
sendAsRetry bool
}
func (echCfg echConfig) MarshalBinary() ([]byte, error) {