diff --git a/Dockerfile b/Dockerfile index 1308fcc..98dcb1a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,4 +12,6 @@ FROM scratch COPY --from=builder /checker-delegation /checker-delegation USER 65534:65534 EXPOSE 8080 +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD ["/checker-delegation", "-healthcheck"] ENTRYPOINT ["/checker-delegation"] diff --git a/README.md b/README.md index 6e1a5f4..db0667f 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ DNS queries to whatever name servers (and glue addresses) the parent zone advertises for the target. It is meant to run on a trusted network, reachable only by the happyDomain instance that drives it. Restrict access via a reverse proxy with authentication, a network ACL, or by -binding the listener to a private interface — do not expose it directly +binding the listener to a private interface; do not expose it directly to the public internet. ## Options diff --git a/checker/dns.go b/checker/dns.go index 0d23761..07f1325 100644 --- a/checker/dns.go +++ b/checker/dns.go @@ -248,4 +248,3 @@ func dsEqual(a, b *dns.DS) bool { a.DigestType == b.DigestType && strings.EqualFold(a.Digest, b.Digest) } - diff --git a/checker/helpers_test.go b/checker/helpers_test.go index f0bc26a..1773854 100644 --- a/checker/helpers_test.go +++ b/checker/helpers_test.go @@ -9,9 +9,9 @@ import ( func TestDiffStringSets(t *testing.T) { cases := []struct { - name string - want, got []string - missing, extra []string + name string + want, got []string + missing, extra []string }{ { name: "identical", diff --git a/checker/rule.go b/checker/rule.go index 26851cf..42f29c2 100644 --- a/checker/rule.go +++ b/checker/rule.go @@ -248,6 +248,13 @@ func (r *inBailiwickGlueRule) Evaluate(ctx context.Context, obs sdk.ObservationG if errState != nil { return errState } + if len(data.ParentViews) == 0 { + return []sdk.CheckState{{ + Status: sdk.StatusUnknown, + Code: "delegation_missing_glue", + Message: "no parent server was queried", + }} + } var out []sdk.CheckState for _, v := range data.ParentViews { if v.UDPNSError != "" { @@ -275,8 +282,13 @@ func (r *inBailiwickGlueRule) Evaluate(ctx context.Context, obs sdk.ObservationG } } } - // No in-bailiwick NS means glue is not mandatory; stay silent rather - // than advertising a check that doesn't apply. + if len(out) == 0 { + return []sdk.CheckState{{ + Status: sdk.StatusOK, + Code: "delegation_missing_glue", + Message: "no in-bailiwick NS, glue not required", + }} + } return out } diff --git a/checker/rule_test.go b/checker/rule_test.go index 62a050c..86327dc 100644 --- a/checker/rule_test.go +++ b/checker/rule_test.go @@ -109,9 +109,9 @@ func TestNSMatchesDeclaredRule(t *testing.T) { DelegatedFQDN: "www.example.com.", DeclaredNS: []string{"ns1.example.net.", "ns2.example.net."}, ParentViews: []ParentView{ - {Server: "p1:53", NS: []string{"ns1.example.net.", "ns2.example.net."}}, // match - {Server: "p2:53", NS: []string{"ns1.example.net.", "ns3.example.net."}}, // mismatch - {Server: "p3:53", UDPNSError: "timeout"}, // skipped + {Server: "p1:53", NS: []string{"ns1.example.net.", "ns2.example.net."}}, // match + {Server: "p2:53", NS: []string{"ns1.example.net.", "ns3.example.net."}}, // mismatch + {Server: "p3:53", UDPNSError: "timeout"}, // skipped }, } states := evalRule(t, r, data, nil) @@ -185,7 +185,7 @@ func TestUnnecessaryGlueRule(t *testing.T) { func TestDSPresentAtParentRule_RequireDS(t *testing.T) { r := &dsPresentAtParentRule{} data := &DelegationData{ - DeclaredDS: []DSRecord{{KeyTag: 1, Algorithm: 8, DigestType: 2, Digest: "AAAA"}}, + DeclaredDS: []DSRecord{{KeyTag: 1, Algorithm: 8, DigestType: 2, Digest: "AAAA"}}, ParentViews: []ParentView{{Server: "p:53"}}, // no DS at parent } t.Run("default is informational", func(t *testing.T) { @@ -382,8 +382,8 @@ func TestDNSKEYMatchesDSRule_Match(t *testing.T) { func TestDNSKEYMatchesDSRule_NoMatch(t *testing.T) { key := &dns.DNSKEY{ - Hdr: dns.RR_Header{Name: "example.com.", Rrtype: dns.TypeDNSKEY, Class: dns.ClassINET}, - Flags: 257, Protocol: 3, Algorithm: dns.RSASHA256, + Hdr: dns.RR_Header{Name: "example.com.", Rrtype: dns.TypeDNSKEY, Class: dns.ClassINET}, + Flags: 257, Protocol: 3, Algorithm: dns.RSASHA256, PublicKey: "AwEAAcMnWBKLuvG/LwnPVykcmpvnntwxfshHlHRhlY0F3oz8AMcuF8gw" + "2Ge56vG9oqVxTzHl4Ss2dEqCQOjFlOVo+pa3JwIO1lUzbQ==", } diff --git a/go.mod b/go.mod index a589c05..02860ac 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.happydns.org/checker-delegation go 1.25.0 require ( - git.happydns.org/checker-sdk-go v1.4.0 + git.happydns.org/checker-sdk-go v1.5.0 github.com/miekg/dns v1.1.72 ) diff --git a/go.sum b/go.sum index fc3939b..2a80023 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.happydns.org/checker-sdk-go v1.4.0 h1:sO8EnF3suhNgYLRsbmCZWJOymH/oNMrOUqj3FEzJArs= -git.happydns.org/checker-sdk-go v1.4.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= +git.happydns.org/checker-sdk-go v1.5.0 h1:5uD5Cm6xJ+lwnhbJ09iCXGHbYS9zRh+Yh0NeBHkAPBY= +git.happydns.org/checker-sdk-go v1.5.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=