checker-tls/checker/rule.go

88 lines
2.6 KiB
Go

package checker
import (
"context"
"fmt"
"sort"
sdk "git.happydns.org/checker-sdk-go/checker"
)
// Rules returns the full list of CheckRules exposed by the TLS checker.
// Each rule covers a single concern (reachability, handshake, chain, hostname,
// expiry, TLS version, STARTTLS advertisement, cipher suite, …) so the UI can
// surface a passing-list rather than a single aggregated code.
func Rules() []sdk.CheckRule {
return []sdk.CheckRule{
&endpointsDiscoveredRule{},
&reachabilityRule{},
&tlsHandshakeRule{},
&starttlsAdvertisedRule{},
&starttlsSupportedRule{},
&peerCertificateRule{},
&chainValidityRule{},
&hostnameMatchRule{},
&expiryRule{},
&tlsVersionRule{},
&cipherSuiteRule{},
}
}
// loadData fetches the TLS observation. On error, returns a single error
// state the caller should emit.
func loadData(ctx context.Context, obs sdk.ObservationGetter) (*TLSData, *sdk.CheckState) {
var data TLSData
if err := obs.Get(ctx, ObservationKeyTLSProbes, &data); err != nil {
return nil, &sdk.CheckState{
Status: sdk.StatusError,
Message: fmt.Sprintf("failed to load tls_probes observation: %v", err),
Code: "tls.observation_error",
}
}
return &data, nil
}
// sortedRefs returns the probe refs in deterministic order. Rules iterate
// this sorted list so CheckState output is stable.
func sortedRefs(data *TLSData) []string {
refs := make([]string, 0, len(data.Probes))
for ref := range data.Probes {
refs = append(refs, ref)
}
sort.Strings(refs)
return refs
}
// subjectOf formats the UI-facing subject for a single probe.
func subjectOf(p TLSProbe) string {
return fmt.Sprintf("%s://%s", p.Type, p.Endpoint)
}
// metaOf returns a compact meta map to attach to a CheckState.
func metaOf(p TLSProbe) map[string]any {
m := map[string]any{
"type": p.Type,
"host": p.Host,
"port": p.Port,
"sni": p.SNI,
}
if p.TLSVersion != "" {
m["tls_version"] = p.TLSVersion
}
return m
}
// passState / infoState / unknownState helpers.
func passState(code, message string) sdk.CheckState {
return sdk.CheckState{Status: sdk.StatusOK, Code: code, Message: message}
}
func unknownState(code, message string) sdk.CheckState {
return sdk.CheckState{Status: sdk.StatusUnknown, Code: code, Message: message}
}
// emptyCaseState returns a single state describing "no probes to evaluate".
// Rules call this when len(data.Probes) == 0 to avoid returning an empty
// slice (see CheckRule.Evaluate contract).
func emptyCaseState(code string) sdk.CheckState {
return unknownState(code, "No TLS endpoints have been discovered for this target yet.")
}