MCPcopy
hub / github.com/caddyserver/caddy / rotateECHKeys

Method rotateECHKeys

modules/caddytls/ech.go:190–280  ·  view source on GitHub ↗

rotateECHKeys updates the ECH keys/configs that are outdated if rotation is needed. It should be called in a write lock on ech.configsMu. If a lock is already obtained in storage, then pass true for storageSynced. This function sets/updates the stdlib-ready key list only if a rotation occurs.

(ctx caddy.Context, logger *zap.Logger, storageSynced bool)

Source from the content-addressed store, hash-verified

188//
189// This function sets/updates the stdlib-ready key list only if a rotation occurs.
190func (ech *ECH) rotateECHKeys(ctx caddy.Context, logger *zap.Logger, storageSynced bool) error {
191 storage := ctx.Storage()
192
193 // all existing configs are now loaded; rotate keys "regularly" as recommended by the spec
194 // (also: "Rotating too frequently limits the client anonymity set." - but the more server
195 // names, the more frequently rotation can be done safely)
196 const (
197 rotationInterval = 24 * time.Hour * 30
198 deleteAfter = 24 * time.Hour * 90
199 )
200
201 if !ech.rotationNeeded(rotationInterval, deleteAfter) {
202 return nil
203 }
204
205 // sync this operation across cluster if not already
206 if !storageSynced {
207 if err := storage.Lock(ctx, echStorageLockName); err != nil {
208 return err
209 }
210 defer func() {
211 if err := storage.Unlock(ctx, echStorageLockName); err != nil {
212 logger.Error("unable to unlock ECH rotation in storage", zap.Error(err))
213 }
214 }()
215 }
216
217 // update what storage has, in case another instance already updated things
218 if _, err := ech.setConfigsFromStorage(ctx, logger); err != nil {
219 return fmt.Errorf("updating ECH keys from storage: %v", err)
220 }
221
222 // iterate the updated list and do any updates as needed
223 for publicName := range ech.configs {
224 for i := 0; i < len(ech.configs[publicName]); i++ {
225 cfg := ech.configs[publicName][i]
226 if time.Since(cfg.meta.Created) >= rotationInterval && cfg.meta.Replaced.IsZero() {
227 // key is due for rotation and it hasn't been replaced yet; do that now
228 logger.Debug("ECH config is due for rotation",
229 zap.String("public_name", cfg.RawPublicName),
230 zap.Uint8("id", cfg.ConfigID),
231 zap.Time("created", cfg.meta.Created),
232 zap.Duration("age", time.Since(cfg.meta.Created)),
233 zap.Duration("rotation_interval", rotationInterval))
234
235 // start by generating and storing the replacement ECH config
236 newCfg, err := generateAndStoreECHConfig(ctx, publicName)
237 if err != nil {
238 return fmt.Errorf("generating and storing new replacement ECH config: %w", err)
239 }
240
241 // mark the key as replaced so we don't rotate it again, and instead delete it later
242 ech.configs[publicName][i].meta.Replaced = time.Now()
243
244 // persist the updated metadata
245 metaBytes, err := json.Marshal(ech.configs[publicName][i].meta)
246 if err != nil {
247 return fmt.Errorf("marshaling updated ECH config metadata: %v", err)

Callers 2

ProvisionMethod · 0.95
StartMethod · 0.80

Calls 10

rotationNeededMethod · 0.95
setConfigsFromStorageMethod · 0.95
updateKeyListMethod · 0.95
echMetaKeyFunction · 0.85
StorageMethod · 0.80
DurationMethod · 0.80
ErrorMethod · 0.45
StringMethod · 0.45
DeleteMethod · 0.45

Tested by

no test coverage detected