diff --git a/checker/rules_chain.go b/checker/rules_chain.go index 12e91b6..e3da350 100644 --- a/checker/rules_chain.go +++ b/checker/rules_chain.go @@ -189,24 +189,10 @@ type targetResolvableRule struct{} func (targetResolvableRule) Name() string { return "target_resolvable" } func (targetResolvableRule) Description() string { - return "Verifies that the final target of the alias chain publishes at least one A or AAAA record." + return "Verifies that the final target of the alias chain exists in DNS (returns NOERROR, not NXDOMAIN)." } -func (targetResolvableRule) Options() sdk.CheckerOptionsDocumentation { - return sdk.CheckerOptionsDocumentation{ - UserOpts: []sdk.CheckerOptionDocumentation{ - { - Id: "requireResolvableTarget", - Type: "bool", - Label: "Require resolvable target", - Description: "When enabled, a chain whose final target returns no A/AAAA is reported as critical (otherwise a warning).", - Default: defaultRequireResolvableTarget, - }, - }, - } -} - -func (targetResolvableRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { +func (targetResolvableRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState { data, errState := loadAlias(ctx, obs) if errState != nil { return errState @@ -217,22 +203,14 @@ func (targetResolvableRule) Evaluate(ctx context.Context, obs sdk.ObservationGet if data.ChainTerminated.Reason != TermOK { return skipped("chain did not terminate normally") } - if len(data.FinalA) > 0 || len(data.FinalAAAA) > 0 { - return okState(data.FinalTarget, fmt.Sprintf("target %s resolves to %d address(es)", data.FinalTarget, len(data.FinalA)+len(data.FinalAAAA))) - } - status := sdk.StatusWarn - if sdk.GetBoolOption(opts, "requireResolvableTarget", defaultRequireResolvableTarget) { - status = sdk.StatusCrit - } - rcode := data.FinalRcode - if rcode == "" { - rcode = "no A/AAAA" + if data.FinalRcode != "NXDOMAIN" { + return okState(data.FinalTarget, fmt.Sprintf("target %s exists in DNS", data.FinalTarget)) } return []sdk.CheckState{withHint(sdk.CheckState{ - Status: status, + Status: sdk.StatusCrit, Subject: data.FinalTarget, - Message: fmt.Sprintf("final target %s does not resolve to an address (%s)", data.FinalTarget, rcode), - }, "Point the alias at a name that publishes at least one A or AAAA record, or fix the upstream zone.")} + Message: fmt.Sprintf("final target %s does not exist (NXDOMAIN)", data.FinalTarget), + }, "The alias points at a name that does not exist; create the missing record or update the alias target.")} } type multipleRecordsRule struct{} diff --git a/checker/rules_common.go b/checker/rules_common.go index b267fba..345cfc7 100644 --- a/checker/rules_common.go +++ b/checker/rules_common.go @@ -9,10 +9,9 @@ import ( // Defaults are centralised so Definition's docs and runtime reads cannot drift. const ( - defaultMaxChainLength = 8 - defaultMinTargetTTL = 60 - defaultRequireResolvableTarget = true - defaultAllowApexCNAME = false + defaultMaxChainLength = 8 + defaultMinTargetTTL = 60 + defaultAllowApexCNAME = false defaultRecognizeApexFlattening = true // hintKey is the CheckState.Meta key the HTML report reads to render the diff --git a/checker/rules_test.go b/checker/rules_test.go index 95a1c28..2c8452f 100644 --- a/checker/rules_test.go +++ b/checker/rules_test.go @@ -290,24 +290,25 @@ func TestCnameDnssecRule(t *testing.T) { } func TestTargetResolvableRule(t *testing.T) { - t.Run("ok", func(t *testing.T) { + t.Run("ok when NOERROR with A record", func(t *testing.T) { d := apexKnownData() d.ChainTerminated.Reason = TermOK d.FinalTarget = "target." d.FinalA = []string{"1.2.3.4"} assertSingle(t, run(targetResolvableRule{}, d, nil), sdk.StatusOK) }) - t.Run("crit by default", func(t *testing.T) { + t.Run("ok when NOERROR with no A/AAAA (e.g. service label)", func(t *testing.T) { d := apexKnownData() d.ChainTerminated.Reason = TermOK - d.FinalTarget = "target." - assertSingle(t, run(targetResolvableRule{}, d, nil), sdk.StatusCrit) + d.FinalTarget = "_2772._tcp.znc.example." + assertSingle(t, run(targetResolvableRule{}, d, nil), sdk.StatusOK) }) - t.Run("warn when requireResolvableTarget=false", func(t *testing.T) { + t.Run("crit when NXDOMAIN", func(t *testing.T) { d := apexKnownData() d.ChainTerminated.Reason = TermOK d.FinalTarget = "target." - assertSingle(t, run(targetResolvableRule{}, d, sdk.CheckerOptions{"requireResolvableTarget": false}), sdk.StatusWarn) + d.FinalRcode = "NXDOMAIN" + assertSingle(t, run(targetResolvableRule{}, d, nil), sdk.StatusCrit) }) t.Run("skip when chain did not terminate normally", func(t *testing.T) { d := apexKnownData()