checker-ldap/checker/rules.go

107 lines
2.9 KiB
Go

package checker
import (
"context"
"fmt"
"strings"
sdk "git.happydns.org/checker-sdk-go/checker"
)
// Rules returns the full list of CheckRules exposed by the LDAP checker.
// Each rule covers one concern (SRV discovery, StartTLS posture, anonymous
// access, …) and produces its CheckStates by scanning raw LDAPData fields
// directly -- there is no shared pre-derived Issues slice in between.
func Rules() []sdk.CheckRule {
return []sdk.CheckRule{
&srvDiscoveryRule{},
&endpointReachableRule{},
&encryptedTransportRule{},
&startTLSSupportedRule{},
&ldapsHandshakeRule{},
&startTLSOnLDAPSRule{},
&refusesPlainBindRule{},
&anonymousSearchBlockedRule{},
&rootDSEReadableRule{},
&saslMechanismsRule{},
&protocolVersionRule{},
&ipv6ReachableRule{},
&bindCredentialsRule{},
&baseDNReadRule{},
&tlsQualityRule{},
}
}
// loadLDAPData fetches the LDAP observation. On error, returns a CheckState
// the caller should emit to short-circuit its rule.
func loadLDAPData(ctx context.Context, obs sdk.ObservationGetter) (*LDAPData, *sdk.CheckState) {
var data LDAPData
if err := obs.Get(ctx, ObservationKeyLDAP, &data); err != nil {
return nil, &sdk.CheckState{
Status: sdk.StatusError,
Message: fmt.Sprintf("failed to load LDAP observation: %v", err),
Code: "ldap.observation_error",
}
}
return &data, nil
}
// critState builds a StatusCrit state with an optional fix hint encoded in
// Meta["fix"] so report.go's fixesFromStates can surface it.
func critState(code, message, subject, fix string) sdk.CheckState {
return stateWithFix(sdk.StatusCrit, code, message, subject, fix)
}
func warnState(code, message, subject, fix string) sdk.CheckState {
return stateWithFix(sdk.StatusWarn, code, message, subject, fix)
}
func infoState(code, message, subject, fix string) sdk.CheckState {
return stateWithFix(sdk.StatusInfo, code, message, subject, fix)
}
func stateWithFix(status sdk.Status, code, message, subject, fix string) sdk.CheckState {
st := sdk.CheckState{
Status: status,
Message: message,
Code: code,
Subject: subject,
}
if fix != "" {
st.Meta = map[string]any{"fix": fix}
}
return st
}
func passState(code, message string) sdk.CheckState {
return sdk.CheckState{
Status: sdk.StatusOK,
Message: message,
Code: code,
}
}
func notTestedState(code, message string) sdk.CheckState {
return sdk.CheckState{
Status: sdk.StatusUnknown,
Message: message,
Code: code,
}
}
func optString(opts sdk.CheckerOptions, key string) string {
v, _ := opts[key].(string)
return strings.TrimSpace(v)
}
// ldapsReachable reports whether at least one LDAPS endpoint accepted TCP.
// Used to soften severity of unreachable plain-LDAP endpoints when modern
// clients would use LDAPS anyway.
func ldapsReachable(data *LDAPData) bool {
for _, ep := range data.Endpoints {
if ep.Mode == ModeLDAPS && ep.TCPConnected {
return true
}
}
return false
}