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/hpke"
"github.com/cloudflare/circl/kem" "github.com/cloudflare/circl/kem"
"github.com/libdns/libdns" "github.com/libdns/libdns"
"github.com/zeebo/blake3"
"go.uber.org/zap" "go.uber.org/zap"
"golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte"
@ -382,9 +381,12 @@ func (dnsPub ECHDNSPublisher) Provision(ctx caddy.Context) error {
} }
func (dnsPub ECHDNSPublisher) PublisherKey() string { func (dnsPub ECHDNSPublisher) PublisherKey() string {
h := blake3.New() // TODO: Figure out what the key should be
fmt.Fprintf(h, "%v", dnsPub.provider) // h := blake3.New()
return dnsPub.provider.(caddy.Module).CaddyModule().ID.Name() + ":" + string(h.Sum(nil)) // 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 { 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 // 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; // 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 // most, however, will want to use it to know which inner names are to be
// are associated with the proper ECH configs. // associated with the given ECH config list.
PublishECHConfigList(ctx context.Context, innerNames []string, echConfigList []byte) error PublishECHConfigList(ctx context.Context, innerNames []string, echConfigList []byte) error
} }
type echConfigMeta struct { type echConfigMeta struct {
Created time.Time `json:"created"` Created time.Time `json:"created"`
Publications map[string]time.Time `json:"publications"` // map of publisher ID to timestamp of publication 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 // The key prefix when putting ECH configs in storage. After this

View File

@ -22,7 +22,9 @@ import (
"log" "log"
"net" "net"
"net/http" "net/http"
"path"
"runtime/debug" "runtime/debug"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -430,26 +432,62 @@ func (t *TLS) Start() error {
if err != nil { if err != nil {
return fmt.Errorf("marshaling ECH config list: %v", err) 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 { for _, publisher := range publication.publishers {
dnsNames := publication.DNSNames publisherKey := publisher.PublisherKey()
if dnsNames == nil { for _, cfg := range echCfgList {
// by default, publish for all (non-outer) server names; convert serverNamesSet = cfg.meta.Publications.unpublishedNames(publisherKey, serverNamesSet)
// 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)
} }
if len(serverNamesSet) > 0 {
dnsNamesToPublish := make([]string, 0, len(serverNamesSet))
for name := range serverNamesSet {
dnsNamesToPublish = append(dnsNamesToPublish, name)
} }
dnsNames = serverNames pubTime := time.Now()
} err := publisher.PublishECHConfigList(t.ctx, dnsNamesToPublish, echCfgListBin)
err := publisher.PublishECHConfigList(t.ctx, dnsNames, echCfgListBin)
if err != nil { if err != nil {
t.logger.Error("publishing ECH configuration list", t.logger.Error("publishing ECH configuration list",
zap.Strings("for_dns_names", publication.DNSNames), zap.Strings("for_dns_names", publication.DNSNames),
zap.Error(err)) 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)
}
}
}
} }
} }
} }