Initial commit
This commit is contained in:
commit
beca2fd7eb
21 changed files with 2698 additions and 0 deletions
129
checker/rules_bind.go
Normal file
129
checker/rules_bind.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
sdk "git.happydns.org/checker-sdk-go/checker"
|
||||
)
|
||||
|
||||
// Rules in this file cover the optional bind-credentials workflow (driven
|
||||
// by the bind_dn / bind_password / base_dn options) plus the cross-checker
|
||||
// TLS-quality fold. Like the other rules, they read raw EndpointProbe
|
||||
// fields and downstream observations directly.
|
||||
|
||||
// bindCredentialsRule: optional authenticated bind. Skipped when bind_dn
|
||||
// isn't supplied, and reported as "not tested" when no encrypted endpoint
|
||||
// was available to attempt it on.
|
||||
type bindCredentialsRule struct{}
|
||||
|
||||
func (r *bindCredentialsRule) Name() string { return "ldap.bind_credentials" }
|
||||
func (r *bindCredentialsRule) Description() string {
|
||||
return "Verifies the supplied bind credentials are accepted by the directory (only runs when bind_dn is set)."
|
||||
}
|
||||
|
||||
func (r *bindCredentialsRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
|
||||
if optString(opts, "bind_dn") == "" {
|
||||
return []sdk.CheckState{notTestedState("ldap.bind_credentials.skipped", "Authenticated bind not tested (no bind_dn supplied).")}
|
||||
}
|
||||
data, errSt := loadLDAPData(ctx, obs)
|
||||
if errSt != nil {
|
||||
return []sdk.CheckState{*errSt}
|
||||
}
|
||||
var states []sdk.CheckState
|
||||
attempted := false
|
||||
for _, ep := range data.Endpoints {
|
||||
if !ep.BindAttempted {
|
||||
continue
|
||||
}
|
||||
attempted = true
|
||||
if ep.BindOK {
|
||||
states = append(states, infoState(
|
||||
CodeBindOK,
|
||||
"Bind on "+ep.Address+" succeeded with the provided credentials.",
|
||||
ep.Address,
|
||||
"",
|
||||
))
|
||||
} else {
|
||||
states = append(states, critState(
|
||||
CodeBindFailed,
|
||||
"Bind on "+ep.Address+" failed: "+ep.BindError,
|
||||
ep.Address,
|
||||
"Verify the bind DN exists and the password is current. On AD, check the account is not locked/expired.",
|
||||
))
|
||||
}
|
||||
}
|
||||
if !attempted {
|
||||
return []sdk.CheckState{notTestedState("ldap.bind_credentials.skipped", "Bind not attempted on any endpoint (no encrypted endpoint reachable).")}
|
||||
}
|
||||
if len(states) == 0 {
|
||||
return []sdk.CheckState{passState("ldap.bind_credentials.ok", "Bind succeeded with the provided credentials.")}
|
||||
}
|
||||
return states
|
||||
}
|
||||
|
||||
// baseDNReadRule: optional baseObject read on the supplied base_dn.
|
||||
type baseDNReadRule struct{}
|
||||
|
||||
func (r *baseDNReadRule) Name() string { return "ldap.base_dn_read" }
|
||||
func (r *baseDNReadRule) Description() string {
|
||||
return "Verifies the bound account can read the supplied base DN (only runs when base_dn is set and bind succeeded)."
|
||||
}
|
||||
|
||||
func (r *baseDNReadRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
|
||||
if optString(opts, "base_dn") == "" {
|
||||
return []sdk.CheckState{notTestedState("ldap.base_dn_read.skipped", "Base DN read not tested (no base_dn supplied).")}
|
||||
}
|
||||
data, errSt := loadLDAPData(ctx, obs)
|
||||
if errSt != nil {
|
||||
return []sdk.CheckState{*errSt}
|
||||
}
|
||||
var states []sdk.CheckState
|
||||
attempted := false
|
||||
for _, ep := range data.Endpoints {
|
||||
if !ep.BaseReadAttempted {
|
||||
continue
|
||||
}
|
||||
attempted = true
|
||||
if ep.BaseReadOK {
|
||||
states = append(states, infoState(
|
||||
CodeBaseReadOK,
|
||||
"Base DN read succeeded on "+ep.Address+" (entries="+strconv.Itoa(ep.BaseReadEntries)+").",
|
||||
ep.Address,
|
||||
"",
|
||||
))
|
||||
} else {
|
||||
states = append(states, critState(
|
||||
CodeBaseReadFailed,
|
||||
"Bind succeeded but baseObject read on "+data.BaseDN+" failed: "+ep.BaseReadError,
|
||||
ep.Address,
|
||||
"Verify the bind DN has read access to the base DN -- typically granted via an ACL entry such as `access to dn.subtree=\"<base>\" by dn.exact=\"<bind>\" read`.",
|
||||
))
|
||||
}
|
||||
}
|
||||
if !attempted {
|
||||
return []sdk.CheckState{notTestedState("ldap.base_dn_read.skipped", "Base DN read not attempted (bind did not succeed on any endpoint).")}
|
||||
}
|
||||
return states
|
||||
}
|
||||
|
||||
// tlsQualityRule: folds downstream TLS checker findings onto the LDAP
|
||||
// service. Consumes a related observation (not LDAPData).
|
||||
type tlsQualityRule struct{}
|
||||
|
||||
func (r *tlsQualityRule) Name() string { return "ldap.tls_quality" }
|
||||
func (r *tlsQualityRule) Description() string {
|
||||
return "Folds the downstream TLS checker findings (certificate chain, hostname match, expiry) onto the LDAP service."
|
||||
}
|
||||
|
||||
func (r *tlsQualityRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState {
|
||||
related, _ := obs.GetRelated(ctx, TLSRelatedKey)
|
||||
if len(related) == 0 {
|
||||
return []sdk.CheckState{notTestedState("ldap.tls_quality.skipped", "No related TLS observation available (no TLS checker downstream, or no probe yet).")}
|
||||
}
|
||||
states := tlsStatesFromRelated(related)
|
||||
if len(states) == 0 {
|
||||
return []sdk.CheckState{passState("ldap.tls_quality.ok", "Downstream TLS checker reports no issues on the LDAP endpoints.")}
|
||||
}
|
||||
return states
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue