WIP: publication history

This commit is contained in:
Matthew Holt 2025-03-04 16:40:47 -07:00
parent b987a60f89
commit 57958969c7
No known key found for this signature in database
GPG Key ID: 2A349DD577D586A5
2 changed files with 84 additions and 24 deletions

View File

@ -17,7 +17,6 @@ import (
"github.com/cloudflare/circl/hpke"
"github.com/cloudflare/circl/kem"
"github.com/libdns/libdns"
"github.com/zeebo/blake3"
"go.uber.org/zap"
"golang.org/x/crypto/cryptobyte"
@ -382,9 +381,12 @@ func (dnsPub ECHDNSPublisher) Provision(ctx caddy.Context) error {
}
func (dnsPub ECHDNSPublisher) PublisherKey() string {
h := blake3.New()
fmt.Fprintf(h, "%v", dnsPub.provider)
return dnsPub.provider.(caddy.Module).CaddyModule().ID.Name() + ":" + string(h.Sum(nil))
// TODO: Figure out what the key should be
// h := blake3.New()
// fmt.Fprintf(h, "%v", dnsPub.provider)
// return fmt.Sprintf("%s::%s", dnsPub.provider.(caddy.Module).CaddyModule().ID, h.Sum(nil))
return string(dnsPub.provider.(caddy.Module).CaddyModule().ID)
}
func (dnsPub *ECHDNSPublisher) PublishECHConfigList(ctx context.Context, innerNames []string, configListBin []byte) error {
@ -850,14 +852,34 @@ type ECHPublisher interface {
// Publishes the ECH config list for the given innerNames. Some publishers
// may not need a list of inner/protected names, and can ignore the argument;
// most, however, will want to use it as guidance to ensure the inner names
// are associated with the proper ECH configs.
// most, however, will want to use it to know which inner names are to be
// associated with the given ECH config list.
PublishECHConfigList(ctx context.Context, innerNames []string, echConfigList []byte) error
}
type echConfigMeta struct {
Created time.Time `json:"created"`
Publications map[string]time.Time `json:"publications"` // map of publisher ID to timestamp of publication
Created time.Time `json:"created"`
Publications publicationHistory `json:"publications"`
}
// publicationHistory is a map of publisher key to
// map of inner name to timestamp
type publicationHistory map[string]map[string]time.Time
func (hist publicationHistory) unpublishedNames(publisherKey string, serverNamesSet map[string]struct{}) map[string]struct{} {
innerNamesSet, ok := hist[publisherKey]
if !ok {
// no history of this publisher publishing this config at all, so publish for entire set of names
return serverNamesSet
}
for innerName := range innerNamesSet {
// names in this loop have already had this config published by this publisher,
// so delete them from the set of names to publish for
//
// TODO: Potentially utilize the timestamp (map value) to preserve server name for re-publication if enough time has passed
delete(serverNamesSet, innerName)
}
return serverNamesSet
}
// The key prefix when putting ECH configs in storage. After this

View File

@ -22,7 +22,9 @@ import (
"log"
"net"
"net/http"
"path"
"runtime/debug"
"strconv"
"strings"
"sync"
"time"
@ -430,25 +432,61 @@ func (t *TLS) Start() error {
if err != nil {
return fmt.Errorf("marshaling ECH config list: %v", err)
}
var serverNames []string
// 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 {
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[name] = struct{}{}
}
}
for _, publisher := range publication.publishers {
dnsNames := publication.DNSNames
if dnsNames == nil {
// by default, publish for all (non-outer) server names; convert
// de-duplicated map of server names to a slice
if serverNames == nil {
serverNames = make([]string, 0, len(t.serverNames))
for sn := range t.serverNames {
serverNames = append(serverNames, sn)
publisherKey := publisher.PublisherKey()
for _, cfg := range echCfgList {
serverNamesSet = cfg.meta.Publications.unpublishedNames(publisherKey, serverNamesSet)
}
if len(serverNamesSet) > 0 {
dnsNamesToPublish := make([]string, 0, len(serverNamesSet))
for name := range serverNamesSet {
dnsNamesToPublish = append(dnsNamesToPublish, name)
}
pubTime := time.Now()
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.Error(err))
}
// update publication history
for _, cfg := range echCfgList {
if cfg.meta.Publications == nil {
cfg.meta.Publications = make(publicationHistory)
}
if _, ok := cfg.meta.Publications[publisherKey]; !ok {
cfg.meta.Publications[publisherKey] = make(map[string]time.Time)
}
for _, name := range dnsNamesToPublish {
cfg.meta.Publications[publisherKey][name] = pubTime
}
metaBytes, err := json.Marshal(cfg.meta)
if err != nil {
return fmt.Errorf("marshaling ECH config metadata: %v", err)
}
parentKey := path.Join(echConfigsKey, strconv.Itoa(int(cfg.ConfigID)))
metaKey := path.Join(parentKey, "meta.json")
if err := t.ctx.Storage().Store(t.ctx, metaKey, metaBytes); err != nil {
return fmt.Errorf("storing ECH config metadata: %v", err)
}
}
dnsNames = serverNames
}
err := publisher.PublishECHConfigList(t.ctx, dnsNames, echCfgListBin)
if err != nil {
t.logger.Error("publishing ECH configuration list",
zap.Strings("for_dns_names", publication.DNSNames),
zap.Error(err))
}
}
}