checker-authoritative-consi.../checker/rules_discovery.go

100 lines
3.5 KiB
Go

package checker
import (
"context"
"fmt"
sdk "git.happydns.org/checker-sdk-go/checker"
)
type nsDeclaredRule struct{}
func (r *nsDeclaredRule) Name() string { return "authoritative_consistency.ns_declared" }
func (r *nsDeclaredRule) Description() string {
return "Verifies the service declares at least the recommended number of name servers and that at least one name server could be discovered."
}
func (r *nsDeclaredRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
data, errSt := loadObservation(ctx, obs)
if errSt != nil {
return []sdk.CheckState{*errSt}
}
minNS := sdk.GetIntOption(opts, "minNameServers", 2)
useParentNS := sdk.GetBoolOption(opts, "useParentNS", true)
var findings []Finding
if len(data.DeclaredNS) == 0 && !useParentNS {
findings = append(findings, Finding{
Code: CodeNoNS,
Severity: SeverityCrit,
Message: "no name servers declared in the service and parent cross-check is disabled",
})
}
if len(data.Probed) == 0 {
findings = append(findings, Finding{
Code: CodeNoNS,
Severity: SeverityCrit,
Message: "no authoritative name servers could be discovered (declared list empty and parent query empty)",
})
}
if len(data.DeclaredNS) > 0 && len(data.DeclaredNS) < minNS {
findings = append(findings, Finding{
Code: CodeTooFewNS,
Severity: SeverityWarn,
Message: fmt.Sprintf("only %d name server(s) declared, RFC 1034 recommends at least %d", len(data.DeclaredNS), minNS),
})
}
if len(findings) == 0 {
return []sdk.CheckState{passState("authoritative_consistency.ns_declared.ok", fmt.Sprintf("%d name server(s) declared", len(data.DeclaredNS)))}
}
return findingsToStates(findings)
}
type parentDelegationRule struct{}
func (r *parentDelegationRule) Name() string { return "authoritative_consistency.parent_delegation" }
func (r *parentDelegationRule) Description() string {
return "Cross-checks the NS RRset returned by the parent zone's referral with the NS declared in the service."
}
func (r *parentDelegationRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
useParentNS := sdk.GetBoolOption(opts, "useParentNS", true)
if !useParentNS {
return []sdk.CheckState{notTestedState("authoritative_consistency.parent_delegation.skipped", "Parent delegation cross-check disabled by option.")}
}
data, errSt := loadObservation(ctx, obs)
if errSt != nil {
return []sdk.CheckState{*errSt}
}
var findings []Finding
if data.ParentQueryError != "" {
findings = append(findings, Finding{
Code: CodeParentQueryFailed,
Severity: SeverityWarn,
Message: fmt.Sprintf("parent delegation query failed: %s", data.ParentQueryError),
})
} else if len(data.DeclaredNS) > 0 && len(data.ParentNS) > 0 {
missing, extra := diffStringSets(data.DeclaredNS, data.ParentNS)
if len(missing) > 0 || len(extra) > 0 {
findings = append(findings, Finding{
Code: CodeParentDrift,
Severity: SeverityWarn,
Message: fmt.Sprintf(
"NS RRset at parent does not match declared service: missing=%v extra=%v",
missing, extra,
),
})
}
}
if len(findings) == 0 {
if len(data.ParentNS) == 0 {
return []sdk.CheckState{notTestedState("authoritative_consistency.parent_delegation.skipped", "No parent delegation observed.")}
}
return []sdk.CheckState{passState("authoritative_consistency.parent_delegation.ok", "Parent delegation matches the declared NS list.")}
}
return findingsToStates(findings)
}