No description
  • Go 98.5%
  • Makefile 0.9%
  • Dockerfile 0.6%
Find a file
2026-04-26 21:46:00 +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 Initial commit 2026-04-26 21:46:00 +07:00
go.mod Initial commit 2026-04-26 21:46:00 +07:00
go.sum Initial commit 2026-04-26 21:46:00 +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 Initial commit 2026-04-26 21:46:00 +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.

License

MIT (see LICENSE and NOTICE).