No description
  • Go 98.5%
  • Makefile 0.9%
  • Dockerfile 0.6%
Find a file
2026-04-30 08:56:36 +07:00
checker Initial commit 2026-04-26 21:46:00 +07:00
plugin Initial commit 2026-04-26 21:46:00 +07:00
.gitignore Initial commit 2026-04-26 21:46:00 +07:00
Dockerfile docker: add HEALTHCHECK probing /health 2026-04-26 21:46:24 +07:00
go.mod docker: add HEALTHCHECK probing /health 2026-04-26 21:46:24 +07:00
go.sum docker: add HEALTHCHECK probing /health 2026-04-26 21:46:24 +07:00
LICENSE Initial commit 2026-04-26 21:46:00 +07:00
main.go Initial commit 2026-04-26 21:46:00 +07:00
Makefile Initial commit 2026-04-26 21:46:00 +07:00
NOTICE Initial commit 2026-04-26 21:46:00 +07:00
README.md Include rules section 2026-04-30 08:56:36 +07:00

checker-ldap

LDAP directory checker for happyDomain.

Probes a domain's LDAP deployment end-to-end: SRV discovery (_ldap._tcp, _ldaps._tcp), transport security (StartTLS per RFC 2830, implicit TLS on port 636), RootDSE introspection (supportedSASLMechanisms, supportedControl, supportedLDAPVersion, namingContexts, vendor fingerprint), anonymous exposure (anonymous bind + baseObject search), plaintext-bind refusal posture, and -- when credentials are supplied -- an authenticated bind with an optional baseObject read on a base DN.

TLS certificate chain / SAN / expiry / cipher posture is out of scope -- the dedicated TLS checker handles that. This checker only confirms that a TLS session can be established and records the negotiated TLS version and cipher for context.

We publish each probed endpoint as a DiscoveryEntry of type tls.endpoint.v1 so that checker-tls (or any other consumer of that contract) can run TLS posture checks against them without redoing the SRV lookup. For _ldap._tcp targets we emit STARTTLS: "ldap" with RequireSTARTTLS: true, so a misconfigured server that later drops StartTLS shows up as a CRIT, not a WARN. For _ldaps._tcp we emit direct-TLS endpoints (STARTTLS: "").

The TLS checker's resulting observations (under the tls_probes key) are folded back into our rule aggregation and HTML report via the SDK's ObservationGetter.GetRelated / ReportContext.Related path: a bad certificate on an LDAP endpoint shows up on the LDAP service page, not only in a separate TLS view.

What it checks

For each of _ldap._tcp (with fallback to port 389) and _ldaps._tcp (fallback to port 636):

  • Reachability: TCP connect on each resolved A/AAAA address, per IP family, timing captured.
  • Transport security:
    • On _ldap._tcp: whether the server advertises StartTLS in its RootDSE supportedExtension (OID 1.3.6.1.4.1.1466.20037), whether the StartTLS upgrade succeeds, and whether cleartext simple binds are refused with confidentialityRequired (resultCode 13) per RFC 4513 §5.1.2.
    • On _ldaps._tcp: whether the implicit TLS handshake succeeds.
  • RootDSE introspection:
    • supportedLDAPVersion -- flags a legacy LDAPv2 advertisement.
    • supportedSASLMechanisms -- warns when only PLAIN/LOGIN are offered and when no strong mechanism (SCRAM-*, EXTERNAL, GSSAPI) is present.
    • supportedControl, supportedExtension, namingContexts, vendorName, vendorVersion -- captured for the report.
  • Anonymous exposure:
    • Anonymous bind attempted; result noted.
    • When anonymous bind succeeds and at least one naming context is advertised, a baseObject search is issued on the first naming context. Any returned entry is flagged as ldap.anon.search_allowed -- the DIT is enumerable without credentials.
  • Credential test (optional): when bind_dn and bind_password are supplied, a simple bind is performed only on a TLS-protected channel. When the bind succeeds and base_dn is supplied, a baseObject search is performed on that DN to confirm the account has read access to the intended subtree.

Most common failure scenarios (addressed in the report)

  1. No encrypted endpoint reachableldap.no_encrypted_endpoint / CRIT. Operator must enable either LDAPS or StartTLS.
  2. StartTLS not offered on 389ldap.starttls.missing / CRIT. Server-specific remediation included (OpenLDAP, 389-ds).
  3. StartTLS advertised but upgrade failsldap.starttls.handshake_failed / CRIT. Hints to run the TLS checker for cipher/cert details.
  4. Cleartext bind accepted on 389 without StartTLSldap.plain_bind.accepted / CRIT. Remediation via olcSecurity on OpenLDAP, require_tls on 389-ds.
  5. LDAPS handshake fails on 636ldap.ldaps.handshake_failed / CRIT.
  6. Anonymous search exposes DITldap.anon.search_allowed / WARN.
  7. Only PLAIN/LOGIN SASL offeredldap.sasl.plain_only / WARN.
  8. LDAPv2 still advertisedldap.legacy_v2 / WARN.
  9. RootDSE unreadable on an otherwise working endpointldap.rootdse.unreadable / WARN.
  10. Provided bind DN / password failldap.bind.failed / CRIT -- surfaces credential / lockout issues immediately.

Options

Id Required Description
domain yes Auto-filled from the service scope (domain name).
timeout no Per-endpoint timeout in seconds (default: 10).
bind_dn no DN to bind as. Used only when bind_password is also set.
bind_password no Secret. Bound only after TLS is established; never sent over cleartext.
base_dn no Base DN to test read access against. Requires a successful authenticated bind.

Rules

Code Description Severity
ldap.has_srv Verifies that _ldap._tcp / _ldaps._tcp SRV records are published and resolvable. WARNING
ldap.endpoint_reachable Verifies that every discovered LDAP endpoint accepts a TCP connection. CRITICAL
ldap.has_encrypted_transport Verifies that at least one reachable endpoint offers an encrypted channel (LDAPS or StartTLS). CRITICAL
ldap.starttls_supported Verifies that StartTLS is offered and succeeds on every reachable plain LDAP endpoint. CRITICAL
ldap.ldaps_handshake Verifies that the direct TLS handshake succeeds on every LDAPS endpoint. CRITICAL
ldap.starttls_on_ldaps Flags servers that needlessly advertise StartTLS on the implicit-TLS LDAPS port. INFO
ldap.ipv6_reachable Verifies at least one endpoint is reachable over IPv6. INFO
ldap.refuses_plain_bind Verifies the directory refuses authentication attempts over a cleartext channel. CRITICAL
ldap.anonymous_search_blocked Flags directories that allow anonymous search of the naming context (information disclosure). WARNING
ldap.rootdse_readable Verifies the RootDSE is readable over TLS and advertises naming contexts. WARNING
ldap.sasl_mechanisms Reviews the supportedSASLMechanisms posture (presence of strong mechanisms, absence of password-equivalent ones). WARNING
ldap.protocol_version Flags servers that still advertise the deprecated LDAPv2 protocol. WARNING
ldap.bind_credentials Verifies the supplied bind credentials are accepted by the directory (only runs when bind_dn is set). CRITICAL
ldap.base_dn_read Verifies the bound account can read the supplied base DN (only runs when base_dn is set and bind succeeded). CRITICAL
ldap.tls_quality Folds the downstream TLS checker findings (certificate chain, hostname match, expiry) onto the LDAP service. CRITICAL

License

MIT (see LICENSE and NOTICE).