Skip to content

Active Directory

A Windows domain over LDAP, read-only: the domain controllers and who holds each FSMO role, the members of the privileged groups, the password policy, account counts, and the raw security facts an audit looks for first — accounts open to Kerberoasting or AS-REP roasting, unconstrained delegation, an old krbtgt password, computers without LAPS, Windows releases out of support.

DumbMonit binds with a dedicated service account that is a plain domain user, member of no group. In Active Directory every authenticated user can read what DumbMonit reads; no administrative right is needed, and none should be given. DumbMonit never writes to the directory, never reads a password attribute (of LAPS it reads the expiry date, never the password) and never asks for the replication rights that would expose secrets.

One monitor covers the whole domain: point it at one domain controller and DumbMonit finds the others in the configuration partition.

From the DumbMonit server (or its relay agent) to the device:

PortProtocolPurpose
636TCPLDAPS (default), also to the other domain controllers
389TCPOnly with the StartTLS or plain LDAP option
53UDPOnly with the DNS servers option
53TCPOnly with the DNS servers option

LDAP shows the directory as it is, not the logs of what happened in it. In particular:

  • Replication. The queried domain controller publishes the state of its inbound replication partners (last success, consecutive failures, last error) only to an account holding the Monitor active directory replication right. Without it, the panel says replication status not readable rather than guessing. Only the queried domain controller’s view is shown: to see another one’s partners, add it as a second monitor.
  • Not visible over LDAP: event logs (logons, lockouts as they happen, group changes as events), Group Policy contents, SYSVOL, DNS zones, and the password hashes themselves.
  • Last logon dates come from lastLogonTimestamp, which domain controllers replicate only every 9 to 14 days: an account counted as inactive is inactive to within two weeks.
  • Group membership is read with nesting (the LDAP_MATCHING_RULE_IN_CHAIN rule), through member/memberOf. A primary group set by primaryGroupID is not a member link and is not seen.

Two paces:

  • At every check (each minute by default): connection and bind, the RootDSE (functional levels, clock, synchronisation), the domain object (password policy, machine account quota), the five FSMO roles, the domain controllers from the configuration partition, the privileged groups and their effective members, the Recycle Bin feature, and a TCP connection to the LDAP port of each other domain controller.
  • In the background every inventory_minutes (15 by default): every user and computer account, with only the attributes the findings need, in pages of 500. Enumerating the whole directory every minute would load the domain controllers for nothing.

The first check waits a few seconds for the inventory; on a large domain the account findings appear at the next check.

All metrics are prefixed dumbmonit_ad_.

MetricWhatLabels
bind_ok1 when the service account is accepted, 0 when the domain controller refuses it
connect_seconds, bind_secondstime to open the connection (TLS included) and to bind
tls_verified1 when the certificate is checked
domain_infovalue 1domain, forest, dc
domain_functional_level, forest_functional_level0 (2000) to 7 (2016), 10 (2025)
dc_synchronized, dc_global_catalog_readythe queried domain controller has finished its initial replication, its global catalog is ready
clock_skew_secondsdomain controller clock minus DumbMonit host clock
recycle_bin_enabled1 when the AD Recycle Bin is enabled
dc_countdomain controllers of the domain
dc_infovalue 1dc, site, gc, rodc, os
dc_reachable1 or 0 per domain controller; no series when its name does not resolve from DumbMonitdc
fsmo_rolevalue 1role (schema, domain_naming, pdc, rid, infrastructure), holder
replication_readable1 when inbound replication status can be read
replication_consecutive_failures, replication_last_success_age_secondsper inbound partner and partitionsource, naming_context
privileged_group_members, privileged_group_enabled_memberseffective members, nesting included, groups left outgroup
privileged_group_fingerprinta hash of the members: changes when someone joins or leavesgroup
users_total, users_enabled, users_disabled, users_locked_out, users_password_never_expires, users_staleuser accounts
computers_total, computers_enabled, computers_stale, groups_totalcomputers and groups
admincount_accountsusers protected by AdminSDHolder (adminCount=1)
krbtgt_password_age_secondsage of the krbtgt password
laps_schema, laps_computers, laps_eligible_computersLAPS in the schema; enabled non-DC computers with a LAPS expiry, out of all of them
password_min_length, password_history_length, password_max_age_seconds (0 = never), password_complexity, lockout_threshold, machine_account_quotadefault domain policy
finding_countobjects concerned by a finding, 0 when examined and cleanfinding, severity, title
inventory_age_seconds, scrape_errorsage of the last full inventory; parts of the directory that could not be read

The privileged groups are found by their well-known identifier, not their name, so a domain installed in another language works the same: Domain Admins (RID 512), Enterprise Admins (519) and Schema Admins (518) — the last two only in the forest root domain — and the built-in Administrators, Account Operators, Server Operators, Print Operators and Backup Operators.

Findings are raw facts — how many objects, and which — not a score; the monitor’s security score weighs them. Each has a stable id, a severity on the score’s scale (low, medium, high, critical, shown on the page as Info, Advisory and Warning), a category, a count and up to twenty sample account names. A finding that was examined and found nothing is kept with a count of 0, so a fixed problem turns green instead of disappearing; findings about accounts appear once the first background inventory has completed. They are served as is by GET /api/targets/{id}/ad/findings and published as dumbmonit_ad_finding_count{finding, severity, title}.

idSeverityWhat
unconstrained_delegationcriticalenabled accounts trusted for unconstrained delegation, domain controllers aside
kerberoastable_privilegedcriticalenabled members of privileged groups with a service principal name
asrep_roastable_usershighenabled accounts with Kerberos pre-authentication disabled (DONT_REQ_PREAUTH)
kerberoastable_usershighenabled users with a service principal name
krbtgt_password_agehighkrbtgt password older than 180 days
laps_coveragehigh when LAPS is absent, medium when partialenabled computers (domain controllers aside) without a LAPS password
reversible_encryptionhighusers whose password is stored with reversible encryption
guest_enabledhighthe built-in Guest account (RID 501) is enabled
obsolete_oshighenabled computers on Windows XP, Vista, 7, 8, 2000, Server 2003, 2008 or 2012
dc_unreachablehighdomain controllers whose LDAP port does not answer
replication_failinghighinbound replication partners with consecutive failures
dc_not_synchronizedhighthe queried domain controller has not finished its initial replication
fsmo_role_orphanedhigha FSMO role held by a deleted domain controller
password_policyhigh below 8 characters or without lockout, medium otherwisegaps in the default domain policy against the baseline: 14 characters, a lockout threshold, complexity
privileged_group_sizemediummore than 5 enabled accounts in Domain Admins, Enterprise Admins and Administrators together
stale_accountsmediumenabled users and computers inactive for more than the thresholds
protocol_transition_delegationmediumconstrained delegation with protocol transition
des_onlymediumaccounts restricted to DES Kerberos keys
password_not_requiredmediumusers allowed an empty password (PASSWD_NOTREQD)
privileged_password_never_expiresmediumprivileged users whose password never expires
privileged_stalemediumprivileged users inactive for more than stale_days_users
schema_admins_not_emptymediumSchema Admins has members
functional_level_oldmediumdomain functional level below 2012 R2
clock_skewmediummore than five minutes between the domain controller and DumbMonit
windows10_end_of_supportmediumenabled computers on Windows 10
admincount_orphanslowadminCount=1 users no longer in a privileged group
password_never_expireslowenabled users whose password never expires
machine_account_quotalowany user can join computers to the domain
recycle_bin_disabledlowthe AD Recycle Bin is not enabled

The built-in rules that apply:

  • Domain controller unreachable: another domain controller of the domain stops answering on its LDAP port for five minutes (Warning). The queried one is covered by Monitor unreachable.
  • Active Directory bind failed: the domain controller refuses the service account — expired password, disabled or locked out (Advisory).
  • Privileged group membership changed: someone joined or left a privileged group, nesting included (Advisory, resolves an hour later).
  • Active Directory replication failing: an inbound partner fails for half an hour (Advisory), when replication status is readable.
  • krbtgt password older than 180 days (Info).

The Active Directory panel shows the domain (functional levels, the queried domain controller, clock skew), the domain controllers with their site, roles and whether they answer, the privileged groups with their members, the password policy and account counts, and the findings table with severity words.

Create a read-only service account for DumbMonit

Section titled “Create a read-only service account for DumbMonit”
  1. Active Directory Users and Computers → your domain → Users → New → User: name it as follows, set a long random password and add it to no group.

    svc-dumbmonit
  2. On its Account tab, tick Password never expires, or the bind fails the day the password does.

  3. Optional, to see inbound replication status: grant the account the right listed below on the domain object.

    dsacls "DC=corp,DC=example,DC=com" /G "CORP\svc-dumbmonit:CA;Monitor active directory replication"
  4. For LDAPS, export your certification authority’s certificate as Base-64 encoded X.509 and paste it as CA certificate below.

  5. In DumbMonit, enter a domain controller’s name as its certificate names it (dc1.corp.example.com) and log in as svc-dumbmonit@corp.example.com.

  • Domain Users membership only — No group or delegation is needed to read
  • Monitor active directory replication — Optional: inbound replication status

Vendor documentation: https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/enable-ldap-over-ssl-3rd-certification-authority

A plain domain user is enough: every authenticated user can read what DumbMonit reads. If passwords expire in your domain, DumbMonit raises Active Directory bind failed the day this one does. LDAPS needs a certificate on each domain controller; an enterprise certification authority (AD CS) issues one automatically. The same user from PowerShell, with the Active Directory module:

New-ADUser -Name svc-dumbmonit -UserPrincipalName svc-dumbmonit@corp.example.com -AccountPassword (Read-Host -AsSecureString 'Password') -Enabled $true

The replication right is granted with dsacls because the delegation wizard does not offer it by name. It reveals no password and no secret. DumbMonit only reads: it never writes to the directory, and of LAPS it reads the expiry date, never the password.

To keep the account from opening a session anywhere, deny it Log on locally and Log on through Remote Desktop Services by Group Policy: an LDAP bind is not affected. Do not restrict it with Log On To or Deny access to this computer from the network: the domain controllers would then refuse the bind.

  • The account needs no group beyond Domain Users, which every account has.
  • Do not use a managed service account (gMSA): it cannot do a simple LDAP bind.
  • Do not grant Replicating Directory Changes (or All): that is the DCSync right, which exposes every password hash. DumbMonit never needs it.
  • Monitor active directory replication only allows reading replication metadata (partners, last success, error codes). It is optional.
  • LDAPS (port 636) is the default. Every domain controller of a domain with an enterprise certification authority receives a certificate automatically; without one, the domain controller does not listen on 636. The certificate must carry the name DumbMonit uses: enter the domain controller by its DNS name, not its IP address, unless the certificate lists the address.
  • CA certificate: paste the PEM of the root (or issuing) authority. Only that authority is then trusted. Without it, DumbMonit trusts the public roots and the system store of its container, which do not know a private enterprise authority.
  • StartTLS (port 389) encrypts the same way, after a plain-text greeting. Use it when port 636 is filtered.
  • Accept an unverifiable certificate keeps the connection encrypted but accepts any certificate: anyone on the path could pose as the domain controller and receive the service account’s password.
  • LDAP channel binding (LdapEnforceChannelBinding) concerns NTLM and Kerberos binds over TLS; the simple bind DumbMonit uses is unaffected.

Domain controllers are reached by name (the certificate carries it), and the domain’s other controllers are found by name too. If the DNS server of the DumbMonit host does not know the domain, there are two fixes:

  • DNS servers option: enter the IP addresses of the DNS servers that know the domain, for example 10.0.0.10, 10.0.0.11. Every lookup of this monitor (the controller you entered, the other controllers, Check every domain controller) then goes through them only, over UDP and then TCP. The connection still uses the name for the certificate check. Empty keeps the system resolver. A relay agent does the same on its own side.
  • Or give the whole container those servers, with dns: in docker-compose.yml:
services:
dumbmonit:
dns:
- 10.0.0.10
- 10.0.0.11

If a name is unknown to the servers, the error says so: dc01.corp.local did not resolve through 10.0.0.10.

FieldValue
User namesvc-dumbmonit@corp.example.com, CORP\svc-dumbmonit, or the full DN of the account
Passwordthe account’s password, stored encrypted and never shown again

An empty password is refused: Active Directory would accept it as an unauthenticated bind without checking anything.

OptionDefaultWhat
Connection security (security)ldapsldaps, starttls or plain
Port (port)636, or 389used if the address does not give one
CA certificate (ca_cert)PEM of the authority that issued the domain controllers’ certificates
Accept an unverifiable certificate (insecure_tls)off
Allow plain LDAP (allow_plaintext)offrequired for plain
DNS servers (dns_servers)emptyIPs of the DNS servers used for every lookup of this monitor; empty = system DNS
Check every domain controller (check_all_dcs)onTCP connection to the LDAP port of each domain controller
Inactive user after (stale_days_users)90days
Inactive computer after (stale_days_computers)90days
Full inventory every (inventory_minutes)15minutes, 1 to 1440
Timeout (request_timeout_seconds)8per LDAP operation, 1 to 60
  • TLS certificate rejected: paste the authority’s certificate as CA certificate, and enter the domain controller by a name its certificate lists.
  • Bind refused: wrong user name or password (LDAP result 49): the diagnostic code from the domain controller is translated — password expired, account disabled, locked out, or must change its password at next logon.
  • The domain controller requires a protected connection: LDAP signing is enforced; switch to LDAPS or StartTLS.
  • A domain controller shows as unresolved: its name does not resolve from the DumbMonit host. Fill the DNS servers option with the domain’s DNS servers (see DNS above), or untick Check every domain controller. Unresolved names never raise an alert.
  • Replication status not readable: grant Monitor active directory replication as in step 3, or accept that replication is not shown.