package checker import ( "context" "fmt" sdk "git.happydns.org/checker-sdk-go/checker" ) func Rule() sdk.CheckRule { return &ldapRule{} } type ldapRule struct{} func (r *ldapRule) Name() string { return "ldap_server" } func (r *ldapRule) Description() string { return "Checks discovery, transport security (StartTLS / LDAPS), RootDSE, SASL posture and (optionally) bind credentials of an LDAP directory." } func (r *ldapRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { var data LDAPData if err := obs.Get(ctx, ObservationKeyLDAP, &data); err != nil { return []sdk.CheckState{{ Status: sdk.StatusError, Message: fmt.Sprintf("failed to load LDAP observation: %v", err), Code: "ldap.observation_error", }} } issues := append([]Issue(nil), data.Issues...) // Fold related TLS observations (from a downstream TLS checker, if any) // into the LDAP issue list so cert/chain problems show up on the LDAP // service page without requiring a separate glance at the TLS checker. related, _ := obs.GetRelated(ctx, TLSRelatedKey) issues = append(issues, tlsIssuesFromRelated(related)...) withIssue := make(map[string]bool, len(issues)) out := make([]sdk.CheckState, 0, len(issues)+len(data.Endpoints)) for _, is := range issues { st := sdk.CheckState{ Status: severityToStatus(is.Severity), Message: is.Message, Code: is.Code, Subject: is.Endpoint, } if is.Fix != "" { st.Meta = map[string]any{"fix": is.Fix} } out = append(out, st) if is.Endpoint != "" { withIssue[is.Endpoint] = true } } for _, ep := range data.Endpoints { if withIssue[ep.Address] || !ep.TCPConnected { continue } out = append(out, sdk.CheckState{ Status: sdk.StatusOK, Message: fmt.Sprintf("%s endpoint operational (TLS=%v)", ep.Mode, ep.TLSEstablished), Code: "ldap.ok", Subject: ep.Address, Meta: map[string]any{ "mode": string(ep.Mode), "tls_established": ep.TLSEstablished, "rootdse_read": ep.RootDSERead, }, }) } if len(out) == 0 { return []sdk.CheckState{{ Status: sdk.StatusInfo, Message: "no LDAP endpoint discovered", Code: "ldap.nothing_to_evaluate", }} } return out } func severityToStatus(sev string) sdk.Status { switch sev { case SeverityCrit: return sdk.StatusCrit case SeverityWarn: return sdk.StatusWarn case SeverityInfo: return sdk.StatusInfo default: return sdk.StatusOK } }