Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 238 additions & 0 deletions docs/docs/scenarios/active-directory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# Active Directory & Identity Monitoring

**Goal:** Monitor the Windows identity stack end to end: domain controller replication,
the machine-account secure channel on every domain member, Kerberos KDC availability,
and — via performance counters — the NTDS directory service, AD Certificate Services
and AD FS.

Domain controllers are the most business-critical Windows role there is: when AD is
degraded, "everything is broken" for users while per-service port checks stay green.
The checks below catch the failure modes that matter, in rough order of blast radius.

---

## Prerequisites

Enable the `CheckActiveDirectory` module (and `CheckSystem` for the counter-based
checks) in `nsclient.ini`:

```ini
[/modules]
CheckActiveDirectory = enabled
CheckSystem = enabled ; check_pdh for NTDS / ADCS / ADFS counters
```

All checks in this guide are Windows-only.

---

## Domain controller replication

Replication failures are the classic silent AD killer: a DC that has not replicated
for longer than the tombstone lifetime is permanently orphaned and must be rebuilt.
Run this on **every** domain controller (replication state is per-DC):

```
check_ad_replication
```

### Expected output (healthy)

```
OK: all 6 replication links are healthy|'DC02 DC=example,DC=com'=0;0;4 ...
```

### Alert output

```
CRITICAL: DC02 DC=example,DC=com: 7 failures, last success 2026-08-10 03:11:42|...
```

Defaults: WARNING on the first failed sync (`consecutive_failures > 0`), CRITICAL
after five in a row or 24 hours without a successful sync. On a non-DC the check
returns UNKNOWN ("Not a domain controller"), so it is safe to deploy fleet-wide.

### Customisation

```
# Only alert on prolonged outages, ignore single hiccups:
check_ad_replication "warning=none" "critical=last_success < -24h"

# Check a remote DC:
check_ad_replication server=dc02.example.com
```

---

## Machine-account secure channel (every domain member)

A broken secure channel ("the trust relationship between this workstation and the
primary domain failed") blocks every domain logon on that host. The check actively
verifies the channel, like `Test-ComputerSecureChannel`:

```
check_secure_channel
```

### Expected output

```
OK: secure channel to EXAMPLE via DC01.example.com: OK
```

Workgroup machines return UNKNOWN ("not joined to a domain"), so the same check can
go to the whole fleet. Use `verify=false` for a passive status query that does not
contact the DC.

---

## Kerberos KDC availability

`check_kdc` sends a real (unauthenticated) Kerberos `AS-REQ` to the KDC and expects
a Kerberos answer — typically `KDC_ERR_PREAUTH_REQUIRED`, which is the *healthy*
response. A plain port check cannot see a KDC that accepts connections but no longer
issues tickets; this can.

```
check_kdc
```

### Expected output

```
OK: dc01.example.com: KRB-ERROR KDC_ERR_PREAUTH_REQUIRED (2ms)|'dc01.example.com'=2ms;1000
```

On a domain-joined machine the KDC and realm are discovered automatically; from
anywhere else, name them explicitly — no domain membership, account or Kerberos
configuration is needed:

```
check_kdc server=dc01.example.com server=dc02.example.com realm=EXAMPLE.COM
```

Defaults: WARNING when the round trip exceeds 1 second, CRITICAL when a KDC does not
answer with a well-formed Kerberos message.

---

## NTDS directory service counters (check_pdh)

The directory service exposes rich health counters under the `NTDS` object. Predefine
the interesting ones in `nsclient.ini` so checks are shell-quoting-free and can be
averaged over time (see the [PDH scenario](counters.md) for the mechanics):

```ini
[/settings/system/windows/counters/ad_repl_queue]
collection strategy = rrd
counter = \NTDS\DRA Pending Replication Synchronizations

[/settings/system/windows/counters/ad_ldap_bind_time]
collection strategy = rrd
counter = \NTDS\LDAP Bind Time

[/settings/system/windows/counters/ad_ldap_sessions]
collection strategy = rrd
counter = \NTDS\LDAP Client Sessions

[/settings/system/windows/counters/ad_ldap_searches]
collection strategy = rrd
counter = \NTDS\LDAP Searches/sec
```

Then alert on averages instead of instantaneous spikes:

```
# Replication backlog building up:
check_pdh counter=ad_repl_queue time=5m "warn=value > 50" "crit=value > 500"

# LDAP binds getting slow (milliseconds):
check_pdh counter=ad_ldap_bind_time time=5m "warn=value > 30" "crit=value > 100"

# Session count for capacity trending (no thresholds, perf data only):
check_pdh counter=ad_ldap_sessions time=5m
```

Other counters worth knowing: `\NTDS\DRA Inbound Bytes Total/sec`,
`\NTDS\DRA Outbound Bytes Total/sec` (replication volume), `\NTDS\DS Directory
Reads/sec`, `\NTDS\DS Directory Writes/sec` (directory load), and
`\NTDS\Kerberos Authentications/sec` / `\NTDS\NTLM Authentications/sec` (an NTLM
uptick often means Kerberos trouble).

---

## AD Certificate Services (check_pdh)

A stalled enterprise CA silently breaks certificate auto-enrolment for the whole
estate. The `Certification Authority` counter object (one instance per CA) makes it
visible; `check_certificate` (CheckSecurity) covers the CA's own certificate expiry.

```ini
[/settings/system/windows/counters/ca_failed_requests]
collection strategy = rrd
counter = \Certification Authority(*)\Failed Requests/sec

[/settings/system/windows/counters/ca_request_time]
collection strategy = rrd
counter = \Certification Authority(*)\Request processing time (ms)
```

```
# Any failed request is worth a look, a stream of them is an incident:
check_pdh counter=ca_failed_requests time=5m "warn=value > 0" "crit=value > 1"

# Issuance latency:
check_pdh counter=ca_request_time time=5m "warn=value > 500" "crit=value > 2000"
```

The instance wildcard `(*)` covers the (normally single) CA instance; use
`nscp sys -- --expand-path "\Certification Authority(*)\Requests/sec"` to see yours.

---

## AD FS token issuance (check_pdh)

ADFS outages break SSO to Microsoft 365 and every federated SaaS app. The `AD FS`
counter object exposes issuance rates and failures:

```ini
[/settings/system/windows/counters/adfs_token_requests]
collection strategy = rrd
counter = \AD FS\Token Requests/sec

[/settings/system/windows/counters/adfs_federation_requests]
collection strategy = rrd
counter = \AD FS\Federation Metadata Requests/sec
```

```
# Token issuance flatlining at 0 during business hours usually means broken SSO:
check_pdh counter=adfs_token_requests time=15m

# Pair with a service check on the ADFS service itself:
check_service service=adfssrv
```

On WAP/proxy nodes the counter set is `AD FS Proxy`. Counter availability varies by
ADFS version — validate with `nscp sys -- --validate "AD FS" --all` before wiring
alerts.

---

## Suggested per-role check sets

| Role | Checks |
|-------------------------|----------------------------------------------------------------------------------------------|
| Every domain member | `check_secure_channel` |
| Every domain controller | `check_ad_replication`, `check_kdc`, `check_service service=ntds service=netlogon service=kdc service=dns`, NTDS counters |
| Enterprise CA | `Certification Authority` counters, `check_certificate` on the CA certificate |
| ADFS farm | `AD FS` counters, `check_service service=adfssrv`, `check_certificate` on the token-signing certificate |

---

## Next Steps

- [Performance Counter (PDH) Monitoring](counters.md) — predefined counters, averaging, localisation gotchas
- [Host Security Posture](security-posture.md) — `check_certificate` for CA / token-signing certificate expiry
- [Service & Process Monitoring](service-monitoring.md) — keep `ntds`, `netlogon`, `kdc`, `adfssrv` running
- [Reference: CheckActiveDirectory](../reference/check/CheckActiveDirectory.md) — full command reference
1 change: 1 addition & 0 deletions docs/docs/scenarios/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Each scenario follows the same structure:
| Scenario | Description |
|-------------------------------------------------|---------------------------------------------------------------------------------|
| [Host Security Posture](security-posture.md) | Certificate expiry/hygiene and logon sessions (Windows & Linux), plus Windows firewall, antivirus, BitLocker and Secure Boot |
| [Active Directory & Identity](active-directory.md) | DC replication, machine secure channel, Kerberos KDC probing, plus NTDS/ADCS/ADFS performance counters |

### Monitoring Server Integration

Expand Down
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ nav:
- SQL Server Monitoring: scenarios/sql-server.md
- Network Checks: scenarios/network-checks.md
- Host Security Posture: scenarios/security-posture.md
- Active Directory & Identity: scenarios/active-directory.md
- Active Monitoring with NRPE: scenarios/nrpe.md
- Passive Monitoring (NSCA/NRDP): scenarios/passive-monitoring-nsca.md
- Passive Monitoring (NSCA-NG): scenarios/passive-monitoring-nsca-ng.md
Expand Down
44 changes: 44 additions & 0 deletions docs/samples/CheckActiveDirectory_check_ad_replication_desc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#### About `check_ad_replication`

`check_ad_replication` reads the inbound replication state of a domain
controller straight from the directory service (`DsReplicaGetInfo`, the same
source `repadmin /showrepl` uses). Each inbound replication link — a (naming
context, source DC) pair — becomes one row: when it last attempted and last
managed to sync, and how many attempts in a row have failed.

Replication failures are the classic silent AD killer: a DC that has not
replicated for longer than the tombstone lifetime (typically 60–180 days) is
permanently orphaned and must be rebuilt. This check alerts long before that.

Keywords (one row per inbound replication link):

| Keyword | Description |
|------------------------|-------------------------------------------------------------------|
| `naming_context` | The replicated partition DN (e.g. `DC=example,DC=com`) |
| `source` | The source domain controller of the link |
| `source_dsa` | Full DN of the source directory service agent |
| `source_address` | Transport address of the source (GUID-based DNS name) |
| `last_attempt` | When a sync was last attempted (date) |
| `last_success` | When a sync last succeeded (date; epoch 0 = never) |
| `consecutive_failures` | Consecutive failed sync attempts (perf data) |
| `last_error` | Win32 result of the last sync attempt (0 = success) |
| `last_error_message` | Human readable message for `last_error` (empty when ok) |
| `failed` | True when the last sync attempt failed |

Defaults: **WARNING** when `consecutive_failures > 0`, **CRITICAL** when
`consecutive_failures > 4 or last_success < -24h`. A link that has *never*
synced trips the 24-hour rule by design.

Options: `server=<dc>` checks another domain controller (default: the local
machine — replication state is per-DC, so run the check on every DC).

**Not-a-DC contract:** run without `server=` on a host that does not run the
directory service, the check returns **UNKNOWN** with a "Not a domain
controller" message rather than a hard error, so it is safe to deploy
fleet-wide. The contract only applies locally: a `server=` target that cannot
be bound is reported as a plain failure — an explicitly named DC being
unreachable (powered off, firewalled, NTDS stopped) is an outage, not an
ignorable non-DC. Remote targets are first probed on TCP port 135 (the RPC
endpoint mapper) with a 5 second deadline so a black-holed host fails fast
instead of hanging the check. A single-DC domain (no replication partners)
returns **OK** with an explanatory empty-state message.
48 changes: 48 additions & 0 deletions docs/samples/CheckActiveDirectory_check_ad_replication_samples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
**Default check (healthy domain controller):**

```
check_ad_replication
OK: all 6 replication links are healthy|'DC02 DC=example,DC=com'=0;0;4 'DC02 CN=Configuration,DC=example,DC=com'=0;0;4 ...
```

**A partner has been failing for a while:**

```
check_ad_replication
CRITICAL: DC02 DC=example,DC=com: 7 failures, last success 2026-08-10 03:11:42|'DC02 DC=example,DC=com'=7;0;4 ...
```

**Only alert on prolonged outages (ignore single hiccups):**

```
check_ad_replication "warning=none" "critical=last_success < -24h"
OK: all 6 replication links are healthy
```

**Check a remote domain controller:**

```
check_ad_replication server=dc02.example.com
OK: all 6 replication links are healthy
```

**Custom output listing every link and its last error:**

```
check_ad_replication "top-syntax=${status}: ${list}" "detail-syntax=${source} -> ${naming_context}: ${last_error_message}"
WARNING: DC02 -> DC=example,DC=com: The RPC server is unavailable., DC03 -> DC=example,DC=com:
```

**On a host that is not a domain controller (the fleet-wide-safe contract):**

```
check_ad_replication
Not a domain controller: Failed to bind to the directory service on WEB01: 6d9: There are no more endpoints available from the endpoint mapper.
```

**On the only domain controller of a single-DC domain:**

```
check_ad_replication
No replication partners found (single domain controller?)
```
34 changes: 34 additions & 0 deletions docs/samples/CheckActiveDirectory_check_kdc_desc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#### About `check_kdc`

`check_kdc` verifies that a Kerberos KDC is actually issuing responses — not
just that port 88 is open. It sends a real (unauthenticated) `AS-REQ` over TCP
and classifies the answer: an `AS-REP` or any `KRB-ERROR` (typically
`KDC_ERR_PREAUTH_REQUIRED`) proves a live KDC, while silence, a reset or a
non-Kerberos answer means authentication is down even though a port probe
would still pass. Kerberos failure looks like "everything is broken" to users,
so this is the check to point at every domain controller.

The probe uses a throwaway principal (`nscp-probe`) and never completes
authentication: no account, no password and no Kerberos configuration is
needed on the monitoring side.

Keywords (one row per probed KDC):

| Keyword | Description |
|--------------|----------------------------------------------------------------------|
| `kdc` | The host that was probed |
| `realm` | The Kerberos realm the probe requested a ticket for |
| `port` | TCP port probed (default 88) |
| `responding` | True when a well-formed Kerberos answer arrived |
| `response` | What came back (`KRB-ERROR ...`, `AS-REP ...`, or the transport error) |
| `error_code` | KRB-ERROR code from the response (-1 when none) |
| `time` | Round-trip time in milliseconds (perf data) |

Defaults: **WARNING** when `time > 1000`, **CRITICAL** when `responding = 0`.

Options: `server=<host>` (repeatable) picks the KDC(s) to probe and
`realm=<REALM>` the realm; both default to what the domain join discovers
(`DsGetDcName`). On a machine that is not domain-joined, `server=` and
`realm=` are required and the check says so with **UNKNOWN**. `timeout=<ms>`
(default 5000) bounds the probes; all KDCs are probed concurrently, so it also
bounds the whole check even when several KDCs are unreachable.
Loading
Loading