1 Commits
Author SHA1 Message Date
sneak 3baead677c watcher: follow a watched name's CNAME for port and TLS checks (closes #203)
check / check (push) Canceled after 0s
When a watched name's nameservers answer with a CNAME and no address,
the DNS check follows every target they gave with ResolveIPAddresses
and saves all addresses found as cnameAddresses in the hostname state,
so nameservers disagreeing on the target do not change them between
checks. Port and TLS checks use them. A change, also from or to none,
is notified as a CNAME address change; the first check from a state
file without them sends none. When a target cannot be followed, or
none of the name's nameservers answered, the last check's addresses
are kept. The domain check now runs the hostname check for the apex
instead of a copy of it.

Model: opus-5-5
2026-10-02 05:07:54 +00:00
2 changed files with 33 additions and 30 deletions
+25 -30
View File
@@ -12,45 +12,38 @@ import (
"sneak.berlin/go/dnswatcher/internal/watcher"
)
// Each name these tests look up in live DNS, and each zone a CNAME
// points into, has two nameservers, to keep queries few (see the top
// of watcher_test.go). cnameHost is a CNAME into another zone,
// readthedocs.io: its nameservers answer with the CNAME and no address.
const cnameHost = "flask.palletsprojects.com"
// TestCNAMEIntoAnotherZonePortAndTLSChecks checks cnameHost against
// live DNS. Its port and TLS checks must use the addresses at the end
// of its CNAME chain.
// TestCNAMEIntoAnotherZonePortAndTLSChecks runs the port and TLS
// checks on hostname state built here: the name's nameserver answered
// with a CNAME into another zone, and following it found ip1. Both
// checks must use ip1. They look nothing up, so the watcher has no
// resolver.
func TestCNAMEIntoAnotherZonePortAndTLSChecks(t *testing.T) {
t.Parallel()
cfg := defaultTestConfig(t)
cfg.Hostnames = []string{cnameHost}
cfg.Hostnames = []string{host}
_, deps := runChecks(t, cfg, nil)
deps := newTestDeps(t, cfg)
w := watcher.NewForTest(
cfg, deps.state, nil,
deps.portChecker, deps.tlsChecker, deps.notifier,
)
deps.state.SetHostnameState(host, cnameState(ip1))
w.CheckAllPorts(t.Context())
w.RunTLSChecks(t.Context())
snap := deps.state.GetSnapshot()
hs := snap.Hostnames[cnameHost]
if len(hs.CNAMEAddresses) == 0 {
t.Fatalf(
"%s: no addresses saved from following its CNAME; if it "+
"is no longer a CNAME into another zone, this test "+
"needs another name",
cnameHost,
)
ps, ok := snap.Ports[ip1+":443"]
if !ok || !slices.Contains(ps.Hostnames, host) {
t.Errorf("no port state for %s at %s:443", host, ip1)
}
for _, ip := range hs.CNAMEAddresses {
ps, ok := snap.Ports[ip+":443"]
if !ok || !slices.Contains(ps.Hostnames, cnameHost) {
t.Errorf("no port state for %s at %s:443", cnameHost, ip)
}
certKey := ip + ":443:" + cnameHost
if _, ok := snap.Certificates[certKey]; !ok {
t.Errorf("no certificate state %s", certKey)
}
certKey := ip1 + ":443:" + host
if _, ok := snap.Certificates[certKey]; !ok {
t.Errorf("no certificate state %s", certKey)
}
}
@@ -88,7 +81,9 @@ func TestCNAMEThatCannotBeFollowedKeepsPrevious(t *testing.T) {
// followLive follows in live DNS the CNAMEs in a name's records, built
// from records, and returns the addresses saved for the name. The
// previous check saved oldIP, which is kept when a target cannot be
// followed; that is retried.
// followed; that is retried. The tests point CNAMEs only at names in
// zones with two nameservers, to keep queries few (see the top of
// watcher_test.go).
func followLive(
t *testing.T,
records map[string]map[string][]string,
+8
View File
@@ -372,6 +372,14 @@ func TestFirstRunBaseline(t *testing.T) {
assertNoNotifications(t, deps)
assertStatePopulated(t, deps)
// testHost answers with an address, so the check saves an empty list
// of CNAME addresses for it; nil would mean the check did not look
// at whether to follow a CNAME.
hs, _ := deps.state.GetHostnameState(testHost)
if hs.CNAMEAddresses == nil || len(hs.CNAMEAddresses) != 0 {
t.Errorf("saved CNAME addresses %#v, want []", hs.CNAMEAddresses)
}
}
func assertNoNotifications(