checker-ldap/checker/provider.go

63 lines
1.8 KiB
Go

package checker
import (
sdk "git.happydns.org/checker-sdk-go/checker"
tlsct "git.happydns.org/checker-tls/contract"
)
func Provider() sdk.ObservationProvider {
return &ldapProvider{}
}
type ldapProvider struct{}
func (p *ldapProvider) Key() sdk.ObservationKey {
return ObservationKeyLDAP
}
// DiscoverEntries implements sdk.DiscoveryPublisher.
//
// It publishes TLS endpoint contract entries for every SRV target we found,
// so a downstream TLS checker can verify the certificate chain / SAN /
// expiry on each one without re-doing the SRV lookup. The LDAP checker
// itself only confirms a TLS session can be established -- certificate
// posture lives in the TLS checker.
//
// SNI is set to the bare domain, which is usually the hostname clients
// connect to. On _ldaps._tcp we emit direct-TLS endpoints (STARTTLS="").
// On _ldap._tcp we emit STARTTLS="ldap" so the TLS checker performs an
// RFC 2830 extended-op upgrade before the handshake. RequireSTARTTLS is
// set to true on 389: a misconfigured server that drops StartTLS must
// show up as a CRIT on the TLS side, not a WARN.
func (p *ldapProvider) DiscoverEntries(data any) ([]sdk.DiscoveryEntry, error) {
d, ok := data.(*LDAPData)
if !ok || d == nil {
return nil, nil
}
var out []sdk.DiscoveryEntry
emit := func(recs []SRVRecord, starttls string, require bool) error {
for _, r := range recs {
ep := tlsct.TLSEndpoint{
Host: r.Target,
Port: r.Port,
SNI: d.Domain,
STARTTLS: starttls,
RequireSTARTTLS: require,
}
entry, err := tlsct.NewEntry(ep)
if err != nil {
return err
}
out = append(out, entry)
}
return nil
}
if err := emit(d.SRV.LDAP, "ldap", true); err != nil {
return nil, err
}
if err := emit(d.SRV.LDAPS, "", false); err != nil {
return nil, err
}
return out, nil
}