Initial commit
This commit is contained in:
commit
f27b7397f7
20 changed files with 1471 additions and 0 deletions
137
checker/rule.go
Normal file
137
checker/rule.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||
)
|
||||
|
||||
// Rule returns the rule that aggregates per-endpoint TLS probe outcomes into
|
||||
// a single status for this checker run.
|
||||
func Rule() sdk.CheckRule {
|
||||
return &tlsRule{}
|
||||
}
|
||||
|
||||
type tlsRule struct{}
|
||||
|
||||
func (r *tlsRule) Name() string { return "tls_posture" }
|
||||
|
||||
func (r *tlsRule) Description() string {
|
||||
return "Summarises TLS handshake, certificate validity, hostname match and expiry across all probed endpoints"
|
||||
}
|
||||
|
||||
func (r *tlsRule) ValidateOptions(opts sdk.CheckerOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *tlsRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) sdk.CheckState {
|
||||
var data TLSData
|
||||
if err := obs.Get(ctx, ObservationKeyTLSProbes, &data); err != nil {
|
||||
return sdk.CheckState{
|
||||
Status: sdk.StatusError,
|
||||
Message: fmt.Sprintf("Failed to read tls_probes: %v", err),
|
||||
Code: "tls_observation_error",
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
total = len(data.Probes)
|
||||
okCount int
|
||||
warnCount int
|
||||
critCount int
|
||||
firstCrit string
|
||||
firstWarn string
|
||||
)
|
||||
|
||||
// Steady state when no producer has published entries for this target
|
||||
// yet (or when the last producer run cleared them). Report Unknown so
|
||||
// we don't flap red during the eventual-consistency window between a
|
||||
// fresh enrollment and the first producer cycle.
|
||||
if total == 0 {
|
||||
return sdk.CheckState{
|
||||
Status: sdk.StatusUnknown,
|
||||
Message: "No TLS endpoints have been discovered for this target yet",
|
||||
Code: "tls_no_endpoints",
|
||||
}
|
||||
}
|
||||
for _, p := range data.Probes {
|
||||
worst, critMsg, warnMsg := summarize(p.Issues)
|
||||
switch worst {
|
||||
case SeverityCrit:
|
||||
critCount++
|
||||
if firstCrit == "" {
|
||||
firstCrit = fmt.Sprintf("%s (%s)", p.Endpoint, critMsg)
|
||||
}
|
||||
case SeverityWarn:
|
||||
warnCount++
|
||||
if firstWarn == "" {
|
||||
firstWarn = fmt.Sprintf("%s (%s)", p.Endpoint, warnMsg)
|
||||
}
|
||||
default:
|
||||
okCount++
|
||||
}
|
||||
}
|
||||
|
||||
meta := map[string]any{
|
||||
"probes": total,
|
||||
"ok": okCount,
|
||||
"warn": warnCount,
|
||||
"crit": critCount,
|
||||
}
|
||||
|
||||
switch {
|
||||
case critCount > 0:
|
||||
return sdk.CheckState{
|
||||
Status: sdk.StatusCrit,
|
||||
Message: fmt.Sprintf("%d/%d TLS endpoint(s) have critical issues: %s", critCount, total, firstCrit),
|
||||
Code: "tls_critical",
|
||||
Meta: meta,
|
||||
}
|
||||
case warnCount > 0:
|
||||
return sdk.CheckState{
|
||||
Status: sdk.StatusWarn,
|
||||
Message: fmt.Sprintf("%d/%d TLS endpoint(s) have warnings: %s", warnCount, total, firstWarn),
|
||||
Code: "tls_warning",
|
||||
Meta: meta,
|
||||
}
|
||||
default:
|
||||
return sdk.CheckState{
|
||||
Status: sdk.StatusOK,
|
||||
Message: fmt.Sprintf("%d TLS endpoint(s) OK", total),
|
||||
Code: "tls_ok",
|
||||
Meta: meta,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// summarize walks the issues once and returns (worst severity, first
|
||||
// critical message, first warning message). Picking the messages during the
|
||||
// same pass avoids a second iteration in the caller.
|
||||
func summarize(issues []Issue) (worst, firstCrit, firstWarn string) {
|
||||
for _, is := range issues {
|
||||
msg := is.Message
|
||||
if msg == "" {
|
||||
msg = is.Code
|
||||
}
|
||||
switch is.Severity {
|
||||
case SeverityCrit:
|
||||
worst = SeverityCrit
|
||||
if firstCrit == "" {
|
||||
firstCrit = msg
|
||||
}
|
||||
case SeverityWarn:
|
||||
if worst == "" || worst == SeverityInfo {
|
||||
worst = SeverityWarn
|
||||
}
|
||||
if firstWarn == "" {
|
||||
firstWarn = msg
|
||||
}
|
||||
case SeverityInfo:
|
||||
if worst == "" {
|
||||
worst = SeverityInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue