134 lines
3.5 KiB
Go
134 lines
3.5 KiB
Go
// This file is part of the happyDomain (R) project.
|
|
// Copyright (c) 2020-2026 happyDomain
|
|
// Authors: Pierre-Olivier Mercier, et al.
|
|
//
|
|
// This program is offered under a commercial and under the AGPL license.
|
|
// For commercial licensing, contact us at <contact@happydomain.org>.
|
|
//
|
|
// For AGPL licensing:
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package checker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// failLevels associates each check name with the severity it raises when it
|
|
// fails. Names must match those produced by checkServerAddr.
|
|
var failLevels = map[string]int{
|
|
"AXFR refused": statusCrit,
|
|
"IXFR refused": statusWarn,
|
|
"No recursion": statusWarn,
|
|
"ANY handled (RFC 8482)": statusWarn,
|
|
"Is authoritative": statusInfo,
|
|
}
|
|
|
|
// Rule returns a new NS restrictions evaluation rule.
|
|
func Rule() sdk.CheckRule {
|
|
return &nsRule{}
|
|
}
|
|
|
|
type nsRule struct{}
|
|
|
|
func (r *nsRule) Name() string { return "ns_restrictions_check" }
|
|
|
|
func (r *nsRule) Description() string {
|
|
return "Checks nameservers for AXFR/IXFR acceptance, recursion availability, RFC 8482 ANY handling and authoritative status"
|
|
}
|
|
|
|
func (r *nsRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) sdk.CheckState {
|
|
var report NSRestrictionsReport
|
|
if err := obs.Get(ctx, ObservationKeyNSRestrictions, &report); err != nil {
|
|
return sdk.CheckState{
|
|
Status: sdk.StatusError,
|
|
Message: fmt.Sprintf("Failed to get NS restrictions data: %v", err),
|
|
Code: "ns_restrictions_error",
|
|
}
|
|
}
|
|
|
|
overall := statusOK
|
|
var summaryParts []string
|
|
|
|
for _, srv := range report.Servers {
|
|
serverWorst := statusOK
|
|
for _, item := range srv.Checks {
|
|
if item.OK {
|
|
continue
|
|
}
|
|
level, ok := failLevels[item.Name]
|
|
if !ok {
|
|
// Unknown check (e.g. "DNS resolution", "IPv6 connectivity")
|
|
// — treat a failure as a warning by default.
|
|
level = statusWarn
|
|
}
|
|
if level > serverWorst {
|
|
serverWorst = level
|
|
}
|
|
}
|
|
|
|
if serverWorst > overall {
|
|
overall = serverWorst
|
|
}
|
|
|
|
label := srv.Name
|
|
if srv.Address != "" {
|
|
label = fmt.Sprintf("%s (%s)", srv.Name, srv.Address)
|
|
}
|
|
summaryParts = append(summaryParts, fmt.Sprintf("%s: %s", label, statusName(serverWorst)))
|
|
}
|
|
|
|
return sdk.CheckState{
|
|
Status: toSDKStatus(overall),
|
|
Message: strings.Join(summaryParts, " | "),
|
|
Code: "ns_restrictions_result",
|
|
Meta: map[string]any{
|
|
"servers": report.Servers,
|
|
},
|
|
}
|
|
}
|
|
|
|
func statusName(s int) string {
|
|
switch s {
|
|
case statusOK:
|
|
return "OK"
|
|
case statusInfo:
|
|
return "INFO"
|
|
case statusWarn:
|
|
return "WARN"
|
|
case statusCrit:
|
|
return "CRITICAL"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
func toSDKStatus(s int) sdk.Status {
|
|
switch s {
|
|
case statusOK:
|
|
return sdk.StatusOK
|
|
case statusInfo:
|
|
return sdk.StatusInfo
|
|
case statusWarn:
|
|
return sdk.StatusWarn
|
|
case statusCrit:
|
|
return sdk.StatusCrit
|
|
default:
|
|
return sdk.StatusUnknown
|
|
}
|
|
}
|