Compare commits
2 commits
56db4cc59d
...
1493ef4d3f
| Author | SHA1 | Date | |
|---|---|---|---|
| 1493ef4d3f | |||
| 52a3e56c4f |
5 changed files with 27 additions and 47 deletions
|
|
@ -400,12 +400,6 @@ func observeApex(ctx context.Context, data *AliasData, servers []string, apex st
|
||||||
|
|
||||||
if (hasA || hasAAAA) && !data.ApexHasCNAME {
|
if (hasA || hasAAAA) && !data.ApexHasCNAME {
|
||||||
data.ApexFlattening = true
|
data.ApexFlattening = true
|
||||||
// Synthesize a pseudo-hop so the report's chain view shows the ALIAS
|
|
||||||
// indirection that would otherwise be invisible from the wire.
|
|
||||||
data.Chain = append(data.Chain, ChainHop{
|
|
||||||
Owner: lowerFQDN(apex),
|
|
||||||
Kind: KindALIAS,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,15 @@ func buildReportView(data *AliasData, states []sdk.CheckState) *reportView {
|
||||||
v.FinalAddresses = append(v.FinalAddresses, data.FinalA...)
|
v.FinalAddresses = append(v.FinalAddresses, data.FinalA...)
|
||||||
v.FinalAddresses = append(v.FinalAddresses, data.FinalAAAA...)
|
v.FinalAddresses = append(v.FinalAddresses, data.FinalAAAA...)
|
||||||
|
|
||||||
for i, h := range data.Chain {
|
chain := data.Chain
|
||||||
|
if data.ApexFlattening {
|
||||||
|
chain = append(chain, ChainHop{
|
||||||
|
Owner: data.Apex,
|
||||||
|
Kind: KindALIAS,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, h := range chain {
|
||||||
step := chainStep{
|
step := chainStep{
|
||||||
Index: i + 1,
|
Index: i + 1,
|
||||||
Owner: h.Owner,
|
Owner: h.Owner,
|
||||||
|
|
@ -112,7 +120,7 @@ func buildReportView(data *AliasData, states []sdk.CheckState) *reportView {
|
||||||
Target: h.Target,
|
Target: h.Target,
|
||||||
TTL: h.TTL,
|
TTL: h.TTL,
|
||||||
Server: h.Server,
|
Server: h.Server,
|
||||||
IsLast: i == len(data.Chain)-1,
|
IsLast: i == len(chain)-1,
|
||||||
}
|
}
|
||||||
switch h.Kind {
|
switch h.Kind {
|
||||||
case KindCNAME:
|
case KindCNAME:
|
||||||
|
|
|
||||||
|
|
@ -189,24 +189,10 @@ type targetResolvableRule struct{}
|
||||||
|
|
||||||
func (targetResolvableRule) Name() string { return "target_resolvable" }
|
func (targetResolvableRule) Name() string { return "target_resolvable" }
|
||||||
func (targetResolvableRule) Description() string {
|
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 {
|
func (targetResolvableRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState {
|
||||||
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 {
|
|
||||||
data, errState := loadAlias(ctx, obs)
|
data, errState := loadAlias(ctx, obs)
|
||||||
if errState != nil {
|
if errState != nil {
|
||||||
return errState
|
return errState
|
||||||
|
|
@ -217,22 +203,14 @@ func (targetResolvableRule) Evaluate(ctx context.Context, obs sdk.ObservationGet
|
||||||
if data.ChainTerminated.Reason != TermOK {
|
if data.ChainTerminated.Reason != TermOK {
|
||||||
return skipped("chain did not terminate normally")
|
return skipped("chain did not terminate normally")
|
||||||
}
|
}
|
||||||
if len(data.FinalA) > 0 || len(data.FinalAAAA) > 0 {
|
if data.FinalRcode != "NXDOMAIN" {
|
||||||
return okState(data.FinalTarget, fmt.Sprintf("target %s resolves to %d address(es)", data.FinalTarget, len(data.FinalA)+len(data.FinalAAAA)))
|
return okState(data.FinalTarget, fmt.Sprintf("target %s exists in DNS", data.FinalTarget))
|
||||||
}
|
|
||||||
status := sdk.StatusWarn
|
|
||||||
if sdk.GetBoolOption(opts, "requireResolvableTarget", defaultRequireResolvableTarget) {
|
|
||||||
status = sdk.StatusCrit
|
|
||||||
}
|
|
||||||
rcode := data.FinalRcode
|
|
||||||
if rcode == "" {
|
|
||||||
rcode = "no A/AAAA"
|
|
||||||
}
|
}
|
||||||
return []sdk.CheckState{withHint(sdk.CheckState{
|
return []sdk.CheckState{withHint(sdk.CheckState{
|
||||||
Status: status,
|
Status: sdk.StatusCrit,
|
||||||
Subject: data.FinalTarget,
|
Subject: data.FinalTarget,
|
||||||
Message: fmt.Sprintf("final target %s does not resolve to an address (%s)", data.FinalTarget, rcode),
|
Message: fmt.Sprintf("final target %s does not exist (NXDOMAIN)", data.FinalTarget),
|
||||||
}, "Point the alias at a name that publishes at least one A or AAAA record, or fix the upstream zone.")}
|
}, "The alias points at a name that does not exist; create the missing record or update the alias target.")}
|
||||||
}
|
}
|
||||||
|
|
||||||
type multipleRecordsRule struct{}
|
type multipleRecordsRule struct{}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
const (
|
const (
|
||||||
defaultMaxChainLength = 8
|
defaultMaxChainLength = 8
|
||||||
defaultMinTargetTTL = 60
|
defaultMinTargetTTL = 60
|
||||||
defaultRequireResolvableTarget = true
|
|
||||||
defaultAllowApexCNAME = false
|
defaultAllowApexCNAME = false
|
||||||
defaultRecognizeApexFlattening = true
|
defaultRecognizeApexFlattening = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -290,24 +290,25 @@ func TestCnameDnssecRule(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTargetResolvableRule(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 := apexKnownData()
|
||||||
d.ChainTerminated.Reason = TermOK
|
d.ChainTerminated.Reason = TermOK
|
||||||
d.FinalTarget = "target."
|
d.FinalTarget = "target."
|
||||||
d.FinalA = []string{"1.2.3.4"}
|
d.FinalA = []string{"1.2.3.4"}
|
||||||
assertSingle(t, run(targetResolvableRule{}, d, nil), sdk.StatusOK)
|
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 := apexKnownData()
|
||||||
d.ChainTerminated.Reason = TermOK
|
d.ChainTerminated.Reason = TermOK
|
||||||
d.FinalTarget = "target."
|
d.FinalTarget = "_2772._tcp.znc.example."
|
||||||
assertSingle(t, run(targetResolvableRule{}, d, nil), sdk.StatusCrit)
|
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 := apexKnownData()
|
||||||
d.ChainTerminated.Reason = TermOK
|
d.ChainTerminated.Reason = TermOK
|
||||||
d.FinalTarget = "target."
|
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) {
|
t.Run("skip when chain did not terminate normally", func(t *testing.T) {
|
||||||
d := apexKnownData()
|
d := apexKnownData()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue