Security auditor for Active Directory and Microsoft Entra ID — single static Go binary, over 450 security checks, 9 compliance frameworks.
ETC Collector audits an entire AD forest and outputs a single JSON document with framework-tagged compliance scores ready for ANSSI, CIS, NIST, DISA, HDS, RGPD and NIS2 reporting. No agent. No Microsoft .NET dependency. No telemetry by default.
Cross-platform single binary covering ADCS escalation paths (ESC1–6, ESC8–11), Kerberos delegation abuse, AdminSDHolder tampering and Tier-0 privilege escalation — runs natively on Linux, macOS and Windows.
- Why this tool
- At a glance
- Architecture
- Quick start
- Install
- Operating modes
- Detection coverage
- Compliance frameworks
- CLI reference
- Configuration
- Permissions
- Output JSON schema
- REST API
- Build from source
- Documentation
- License
- Support
A senior identity-security engineer should immediately see what stands out:
- Cross-platform single binary — pure Go, ~50 MB on disk, no .NET, no Python, no JVM. Linux / macOS / Windows × amd64 / arm64.
- AD + Microsoft Entra ID in one run — emit a single JSON document covering both directories with consistent scoring.
- Framework-tagged findings — every detection carries the official compliance controls it satisfies (e.g.
compliance: [{framework: "ANSSI_PA099", control: "R28"}]), and a per-framework score is computed insummary.complianceScores[]. - 24 structured LDAP error codes — instead of opaque Go runtime errors, classified codes such as
LDAP_TLS_IP_SAN_MISSING,LDAP_BIND_INVALID_CREDENTIALS,LDAP_REFERRAL_BAD_BASE_DNare emitted with a fix suggestion and a doc anchor. - Three operating modes from one binary — CLI one-shot for CI/CD, embedded HTTPS server with web UI, or long-running daemon enrolled with the EtcSec SaaS.
- Stable JSON schema — designed to be consumed downstream (SaaS dashboards, custom dashboards, SIEM ingestion).
Detector counts move release to release as detectors are added, split, or retired — see docs/vulnerabilities/README.md for the current generated-catalog totals rather than a number pinned here.
| Metric | Value |
|---|---|
| Detectors | over 450 across Active Directory and Microsoft Entra ID — one binary, everything included |
| Compliance frameworks | 9 scored per audit (PA-099, BP-039, Guide d'hygiène, HDS, RGPD, NIS2, CIS, NIST, DISA) |
| LDAP error codes | 24 structured codes |
| Binary size | ~10 MB compressed, ~50 MB on disk |
| Runtime dependencies | none — pure Go static binary |
| Build target Go version | 1.26+ |
| Supported OS / arch | linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64 |
ETC Collector ships one binary that runs in three distinct modes:
┌───────────────────────────────────────────────────────────────────────┐
│ ETC Collector v3.2.0 │
├───────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌────────────────┐ ┌─────────────────────┐ │
│ │ 1. CLI │ │ 2. Server │ │ 3. SaaS Daemon │ │
│ │ one-shot │ │ standalone │ │ long-running poll │ │
│ ├────────────────┤ ├────────────────┤ ├─────────────────────┤ │
│ │ etc-collector │ │ etc-collector │ │ etc-collector │ │
│ │ audit ad ... │ │ server │ │ daemon │ │
│ │ │ │ │ │ │ │
│ │ → stdout JSON │ │ → HTTPS API │ │ → enrolls with SaaS │ │
│ │ → exit 0/1 │ │ :8443 + GUI │ │ → polls schedule │ │
│ └───────┬────────┘ └───────┬────────┘ └─────────┬───────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ stdout / file localhost:8443 api.etcsec.com │
│ web UI + REST │
│ │
└───────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ Detector │
│ Registry │
│ (450+ checks) │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ AD provider │ │ Entra ID │ │ Future : │
│ ldap:// │ │ MS Graph SDK │ │ Exchange, │
│ ldaps:// │ │ Tenant ID + │ │ Intune, │
│ + SMB SYSVOL│ │ Client cred │ │ Workspace │
└──────────────┘ └──────────────┘ └──────────────┘
| Mode | Best for | Network | Persistence |
|---|---|---|---|
| CLI | CI/CD pipelines, ad-hoc audits, air-gapped environments | Outbound to DC only | None — JSON to stdout |
| Server | Local team dashboard, ad-hoc API integration | Outbound to DC + inbound HTTPS :8443 | Local SQLite (audit history) |
| Daemon | Centrally managed at scale via EtcSec SaaS | Outbound to DC + outbound HTTPS to api.etcsec.com | Local config + enrollment token |
Images are published to two registries — pick either:
Docker Hub etcseccom/etc-collector
or ghcr.io/etcsec-com/etc-collector.
# 1. Pull the Docker image
docker pull etcseccom/etc-collector:latest
# …or from GitHub Container Registry:
# docker pull ghcr.io/etcsec-com/etc-collector:latest
# 2. Audit Active Directory (one-shot)
mkdir -p output && chmod 777 output # the image runs as non-root uid 1000
docker run --rm -v "$(pwd)/output":/output \
etcseccom/etc-collector:latest \
audit ad \
--ldap-url ldaps://dc01.example.com:636 \
--ldap-bind-dn "CN=svc-etccollector,CN=Users,DC=example,DC=com" \
--ldap-bind-password "$LDAP_BIND_PASSWORD" \
--ldap-base-dn "DC=example,DC=com" \
-o /output/audit.json
# 3. Inspect the result
jq '.audit.summary | {findings: .risk.findings.total, score: .risk.score, frameworks: [.complianceScores[].framework]}' output/audit.json
-e LDAP_URL=...does not work here yet.environment-variables.mddocumentsLDAP_URL/LDAP_BIND_DN/LDAP_BIND_PASSWORD/LDAP_BASE_DNas valid for LDAP connections, and they genuinely are forserver/daemonmode — confirmed live:env LDAP_URL=... etc-collector serverpicks them up with no flags. But the one-shotaudit ad/audit azure/discoversubcommands don't bind them (confirmed live, native binary and Docker both): with only those env vars set and no flags, the command fails withrequired flag(s) "ldap-base-dn", "ldap-bind-dn", "ldap-bind-password", "ldap-url" not set. Use explicit--ldap-*flags for one-shot audits as shown above until this is fixed — tracked, not a doc-side workaround.
-o /dev/stdout > audit.jsonalso does not work. Progress logs write to the same stdout stream as the JSON result, corrupting the file mid-object (confirmed live: the resulting file failsjqparsing withInvalid string: control characters). Write to a real path (as above) instead — this is B_192, already tracked.
LDAP_BIND_DN above is a read-only service account you create first in your own Active Directory — see Permissions below for the exact one-time setup, and substitute your own domain for example.com/DC=example,DC=com throughout this page.
See docs/configuration/ad-getting-started.md for a guided walkthrough including DC certificate extraction and 5 connection scenarios.
Every release ships a checksums.sha256 file alongside the binaries.
VERSION=3.2.0
BASE=https://github.com/etcsec-com/etc-collector-com/releases/download/v${VERSION}
# Download the binary + the checksums
curl -LO ${BASE}/etc-collector-${VERSION}-linux-amd64.tar.gz
curl -LO ${BASE}/checksums.sha256
# Verify
sha256sum -c checksums.sha256 --ignore-missing
# → etc-collector-3.2.0-linux-amd64.tar.gz: OKVERSION=3.2.0
curl -LO https://github.com/etcsec-com/etc-collector-com/releases/download/v${VERSION}/etc-collector-${VERSION}-linux-amd64.tar.gz
tar -xzf etc-collector-${VERSION}-linux-amd64.tar.gz
sudo install -m 0755 etc-collector-${VERSION}-linux-amd64/etc-collector /usr/local/bin/etc-collector
etc-collector --versionARCH=$(uname -m | sed 's/x86_64/amd64/')
VERSION=3.2.0
curl -LO https://github.com/etcsec-com/etc-collector-com/releases/download/v${VERSION}/etc-collector-${VERSION}-darwin-${ARCH}.tar.gz
tar -xzf etc-collector-${VERSION}-darwin-${ARCH}.tar.gz
sudo install -m 0755 etc-collector-${VERSION}-darwin-${ARCH}/etc-collector /usr/local/bin/etc-collector
# If Gatekeeper blocks the binary
sudo xattr -rd com.apple.quarantine /usr/local/bin/etc-collector$Version = "3.2.0"
$Base = "https://github.com/etcsec-com/etc-collector-com/releases/download/v$Version"
Invoke-WebRequest -Uri "$Base/etc-collector-$Version-windows-amd64.zip" -OutFile etc-collector.zip
Expand-Archive etc-collector.zip -DestinationPath . -Force
New-Item -ItemType Directory -Force -Path "C:\Program Files\ETCSec" | Out-Null
Move-Item ".\etc-collector-$Version-windows-amd64\etc-collector.exe" "C:\Program Files\ETCSec\etc-collector.exe" -Force
# Optional : install as Windows service
& "C:\Program Files\ETCSec\etc-collector.exe" installThe Windows binary in this release (v3.2.0) is unsigned (Authenticode). No OV code-signing certificate has been issued yet, so Windows SmartScreen will warn and AppLocker/WDAC policies may block the binary — this shipped through the documented
ALLOW_UNSIGNED_RELEASEopt-out. Signing is enforced by default in CI going forward — a release that cannot be signed now fails the build instead of publishing silently unsigned. The checksums file itself is signed: every release publishes achecksums.sha256.bundle(Sigstore keyless signature) alongsidechecksums.sha256— confirmed live against v3.2.0 withcosign verify-blob(2026-09-08). Verify your download against the signed checksum — see Verify the download above.
Pinned to a version, from either registry — see Docker Hub for the tag list.
docker pull etcseccom/etc-collector:3.2.0
# …or: docker pull ghcr.io/etcsec-com/etc-collector:3.2.0
mkdir -p output && chmod 777 output # the image runs as non-root uid 1000
docker run --rm \
-v /etc/ssl/certs:/etc/ssl/certs:ro \
-v "$(pwd)/output":/output \
etcseccom/etc-collector:3.2.0 \
audit ad \
--ldap-url ldaps://dc01.example.com:636 \
--ldap-bind-dn "CN=svc-etccollector,CN=Users,DC=example,DC=com" \
--ldap-bind-password "$LDAP_BIND_PASSWORD" \
--ldap-base-dn "DC=example,DC=com" \
-o /output/audit.json
-e LDAP_*env vars and-o /dev/stdoutdon't work for one-shotaudit/discover— see the callout in Quick start above.
services:
etc-collector:
image: etcseccom/etc-collector:3.2.0
container_name: etc-collector
restart: unless-stopped
ports:
- "8443:8443"
command:
- server
- --ldap-url=ldaps://dc01.example.com:636
- --ldap-bind-dn=CN=svc-etccollector,CN=Users,DC=example,DC=com
- --ldap-bind-password=${LDAP_BIND_PASSWORD}
- --ldap-base-dn=DC=example,DC=com
volumes:
- collector-data:/app/data
- ./keys:/app/keys:ro
volumes:
collector-data:Best for CI/CD, ad-hoc audits, and air-gapped environments. The binary connects, audits, prints JSON, exits.
etc-collector audit ad --ldap-url ldaps://dc:636 ... -o audit.json
echo "exit=$?"Exit codes :
0— audit completed (findings may exist)1— audit failed (LDAP error, network error, file write error...)
Local HTTPS API + embedded GUI. No cloud, no enrollment.
etc-collector server --port 8443
# → https://localhost:8443
# → REST API at /api/v1/*A web UI is served at the root; a REST API allows scripting audits and pulling job results. JWT auth required.
The collector enrolls with the EtcSec SaaS platform, polls for audit commands, and uploads results to your dashboard. Configuration and scheduling are managed centrally.
etc-collector enroll YOUR_TOKEN --saas-url https://api.etcsec.com
sudo systemctl enable --now etcsec-collectorThe daemon also serves the local admin GUI alongside SaaS operations. By default the GUI listens on 127.0.0.1:8443. Use --gui-host 0.0.0.0 to expose it (and configure firewall + access token).
→ See docs/modes/ for the full mode comparison.
One binary, every category included — no edition gating.
| Category | Total | Critical | High | Medium | Low | Info |
|---|---|---|---|---|---|---|
| accounts | 32 | 3 | 14 | 12 | 2 | 1 |
| password | 10 | 4 | 2 | 4 | 0 | 0 |
| kerberos | 14 | 5 | 4 | 4 | 1 | 0 |
| computers | 33 | 10 | 9 | 8 | 3 | 3 |
| groups | 17 | 1 | 7 | 8 | 0 | 1 |
| permissions | 23 | 3 | 10 | 9 | 0 | 1 |
| gpo | 35 | 6 | 9 | 15 | 4 | 1 |
| monitoring | 8 | 0 | 4 | 3 | 1 | 0 |
| network | 11 | 0 | 5 | 1 | 3 | 2 |
| trusts | 7 | 0 | 4 | 3 | 0 | 0 |
| advanced | 49 | 8 | 9 | 19 | 4 | 9 |
| compliance | 87 | 3 | 36 | 35 | 8 | 5 |
| adcs (ESC1–5, ESC8–11) | 9 | 3 | 4 | 2 | 0 | 0 |
| attack-paths | 3 | 2 | 1 | 0 | 0 | 0 |
| Category | Total | Notes |
|---|---|---|
| identity | 28 | MFA, SSPR, hybrid sync, lifecycle, legacy auth, password policy |
| applications | 28 | App registrations, service principals, OAuth consent, SAML certs |
| privileged-access | 23 | PIM, role assignments, eligible vs active, role thresholds |
| conditional-access | 20 | CA policy coverage, exclusions, gaps |
| guest-external | 14 | B2B, invitation policy, stale guests |
| groups | 12 | Role-assignable, dynamic membership, owner gaps |
| config | 5 | Tenant settings, security defaults, user consent |
| compliance (azureCompliance) | 8 | Access reviews, P2 license usage, CIS gaps, terms of use |
| risk-protection | 17 | Identity Protection, leaked credentials, sign-in/user risk policies |
Per-category counts above are a snapshot; the generated catalogs are the source of truth and move as detectors are added, split, or retired.
→ Browse the live catalog: etcsec.com/vulnerabilities — always current, no rebuild needed.
The generated snapshots in this repo — docs/vulnerabilities/active-directory/ and docs/vulnerabilities/azure/ — match the binary you downloaded: over 450 unique detections combined (338 AD + 155 Entra ID, measured 2026-09-07).
ETC Collector tags every finding with the compliance controls it satisfies, then computes per-framework scores in summary.complianceScores[]. Nine frameworks ship in v3.2.0, all referenced against their official publication identifiers.
| Framework key (in JSON) | Coverage | Official publication |
|---|---|---|
ANSSI_PA099 |
90 detectors | ANSSI-PA-099 v1.0 — Recommandations pour l'administration sécurisée des SI reposant sur Microsoft Active Directory (02/10/2023) |
ANSSI_BP039 |
3 detectors | ANSSI-BP-039 v1.0 — Mise en œuvre des fonctionnalités de sécurité de Windows 10 reposant sur la virtualisation (11/2017) |
ANSSI_GUIDE_HYGIENE |
18 detectors | ANSSI Guide d'hygiène informatique (40 mesures essentielles) |
HDS_v1_1 |
40 detectors | Référentiel HDS v1.1 — Hébergement Données de Santé (Agence du Numérique en Santé) |
RGPD |
21 detectors | RGPD article 32 — Sécurité du traitement (UE 2016/679) |
NIS2_FR |
42 detectors | Directive UE 2022/2555 (NIS2), transposition FR loi 2024-449 |
CIS_v8 |
19 detectors | CIS Controls v8.1 (May 2024) + CIS Microsoft Windows Server 2022 Benchmark v3.0.0 |
NIST_800_53 |
20 detectors | NIST SP 800-53 Rev.5 (Sept 2020, patch 2023) — AC, AU, IA control families |
DISA_STIG |
8 detectors | DISA STIG — Active Directory Domain V3R3 |
Each framework also exposes a scope profile so you can run only the detectors relevant to it:
etc-collector audit ad ... --scope-profile compliance-anssi
etc-collector audit ad ... --scope-profile compliance-cis
# ... compliance-anssi-bp039, compliance-anssi-hyg, compliance-hds,
# compliance-rgpd, compliance-nis2, compliance-nist, compliance-disa→ See docs/configuration/compliance.md for the full mapping table and a per-control breakdown.
The full command tree (output of etc-collector --help v3.2.0):
etc-collector
├── audit Run a one-shot security audit
│ ├── ad Audit Active Directory (LDAP / LDAPS / StartTLS)
│ ├── azure Audit Microsoft Entra ID
│ ├── exchange Audit Exchange Online (mailbox delegation, forwarding)
│ ├── intune Audit Microsoft Intune (device compliance, encryption)
│ ├── google Audit Google Workspace (2FA, OAuth, drive sharing)
│ ├── list List available detector categories, profiles and IDs
│ ├── catalog Render the vulnerability catalog markdown from the detector registry
│ └── verify Verify the SHA-256 integrity signature of an audit JSON report
├── discover List assets without running detectors (read-only preview)
├── server Manage the local admin GUI & API server
├── daemon Run in daemon mode (SaaS)
├── enroll Enroll this collector with the SaaS platform
├── unenroll Remove enrollment
├── status Show enrollment status
├── trial Run a one-shot anonymous trial session
├── install Install ETC Collector as a system service
├── uninstall Uninstall the system service
├── upgrade Upgrade the etc-collector binary out-of-process (v3.1.15+)
├── service Manage the running service
├── gui-token Manage GUI access token
├── compliance Compliance catalog diagnostics (see docs/configuration/compliance.md)
├── license Display the software license
├── help Help about any command
└── completion Generate shell autocompletion (bash, zsh, fish, powershell)
Flags below are confirmed live against etc-collector audit ad --help v3.2.0
and grouped by theme for readability — the real --help output is a flat,
alphabetically-sorted list without these section headers:
LDAP connection
--ldap-url string ldap:// or ldaps:// URL (REQUIRED)
--ldap-bind-dn string Bind DN (DN, UPN or NetBIOS form) (REQUIRED)
--ldap-bind-password string Bind password (REQUIRED)
--ldap-base-dn string Search base DN (REQUIRED)
TLS
--ldap-tls-verify Verify LDAP TLS certificates (default true)
--ldap-ca-cert string Path to a PEM file with the CA chain
--ldap-tls-min-version string Min TLS version: 1.0 / 1.1 / 1.2 / 1.3
--ldap-start-tls Upgrade ldap:// (port 389) to TLS via StartTLS
Audit scope
--scope-profile string quick | compliance | pentest |
compliance-anssi | compliance-anssi-bp039 |
compliance-anssi-hyg | compliance-hds |
compliance-rgpd | compliance-nis2 |
compliance-cis | compliance-nist | compliance-disa
--scope-include-categories strings Categories to include (comma-separated)
--scope-exclude-categories strings Categories to exclude
--scope-include-detectors strings Detector IDs to include
--scope-exclude-detectors strings Detector IDs to exclude
--exclusions string Path to an exclusions.yaml file
--exclusions-dry-run Compute exclusions without applying them
Network probes
--enable-network-probes Enable HTTP/HTTPS reach probes for ADCS, DNS AXFR
Output
--format string json | json-pretty (default json)
--include-details Include affected entities (default true)
-o, --output string Output file path (default stdout)
Reproducibility
--as-of string Reference time (RFC3339) recency detectors measure
against. Default: the real moment the audit runs.
Lets a frozen benchmark replay identically on a
later date.
Global
--config string Path to config.yaml
-V, --verbose Enable verbose/debug output
ETC Collector reads configuration in this priority (highest first):
- CLI flags
- Environment variables
config.yaml(searched in./,~/.etc-collector/,/etc/etc-collector/)- Built-in defaults
# Server mode settings (only needed for `etc-collector server`)
server:
host: "127.0.0.1"
port: 8443
tls:
cert: /etc/etc-collector/server.crt
key: /etc/etc-collector/server.key
# Active Directory connection (used by `etc-collector audit ad`)
ldap:
url: ldaps://dc01.example.com:636
bind_dn: "CN=svc-etccollector,CN=Users,DC=example,DC=com"
bind_password: ${LDAP_BIND_PASSWORD} # env var expansion
base_dn: "DC=example,DC=com"
tls_ca_cert: /etc/etc-collector/rootca.pem
tls_verify: true
# Microsoft Entra ID
azure:
tenant_id: ${AZURE_TENANT_ID}
client_id: ${AZURE_CLIENT_ID}
client_secret: ${AZURE_CLIENT_SECRET}
# Default audit scope
audit:
scope_profile: compliance| Variable | Purpose |
|---|---|
LDAP_URL, LDAP_BIND_DN, LDAP_BIND_PASSWORD, LDAP_BASE_DN |
LDAP connection |
AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET |
Entra ID app registration |
AUDIT_INCLUDE_CATEGORIES, AUDIT_EXCLUDE_CATEGORIES |
Equivalent to --scope-* flags |
AUDIT_PROFILE |
Equivalent to --scope-profile |
SERVER_PORT, AUTH_JWT_SECRET |
Server mode |
→ Full reference: docs/configuration/environment-variables.md
A standard Domain Users member is enough for the full audit: LDAP read on objects, SMB read on \\<domain>\SYSVOL (default for Authenticated Users), and read access to audit policy via the registry-replicated GPO objects.
# Create a dedicated, non-rotating, non-delegated read-only account
New-ADUser -Name "svc-etccollector" `
-SamAccountName "svc-etccollector" `
-UserPrincipalName "svc-etccollector@example.com" `
-AccountPassword (Read-Host -AsSecureString "Password") `
-Enabled $true `
-PasswordNeverExpires $true `
-CannotChangePassword $true
Set-ADAccountControl -Identity svc-etccollector -AccountNotDelegated $trueDo not put this account in Domain Admins, Backup Operators, or any privileged group — read-only is the entire model.
APP_NAME="ETC-Collector-Audit"
az ad app create --display-name "$APP_NAME"
APP_ID=$(az ad app list --display-name "$APP_NAME" --query "[0].appId" -o tsv)
az ad app credential reset --id "$APP_ID" --append --years 2
for PERM in \
"Directory.Read.All" \
"User.Read.All" \
"Group.Read.All" \
"Application.Read.All" \
"RoleManagement.Read.All" \
"RoleManagementPolicy.Read.Directory" \
"Policy.Read.All" \
"AuditLog.Read.All" \
"DelegatedPermissionGrant.Read.All" \
"CrossTenantInformation.ReadBasic.All" \
"IdentityRiskyUser.Read.All" \
"Organization.Read.All" \
"UserAuthenticationMethod.Read.All"
do
az ad app permission add --id "$APP_ID" \
--api 00000003-0000-0000-c000-000000000000 \
--api-permissions $(az ad sp show --id 00000003-0000-0000-c000-000000000000 \
--query "appRoles[?value=='$PERM'].id | [0]" -o tsv)=Role
done
az ad app permission admin-consent --id "$APP_ID"→ Step-by-step: docs/configuration/permissions.md, or the guided walkthrough with the full 13-permission table and troubleshooting: docs/configuration/azure-getting-started.md
A single audit produces one JSON document with this top-level shape:
Confirmed live against a real v3.2.0 audit (2026-09-02) — two fields in
earlier versions of this example didn't exist in the real output
(summary.objects.findings and attackGraph.totalPaths) and are fixed
below; metadata is now shown in its real nested shape.
Re-verified live against a real v3.2.0 audit (2026-09-08, T_172, real lab DC):
attackGraph.uniqueNodes is an array of node objects (id/name/type/pathCount/sid), not a
count — fixed below; three more top-level attackGraph fields exist (domain, generatedAt,
version) alongside the already-documented stats-shaped summary. The per-category findings
shape shown below (accounts/computers/... → { "findings": [...] }) is a simplification: on the
real output, several categories (e.g. accounts) group findings under named sub-buckets instead of a
flat array (e.g. accounts.dangerous/accounts.privileged/accounts.service/accounts.status, each
{findings, total}-shaped) — the exact per-category shape isn't re-documented field-by-field here,
this note only flags that { "findings": [...] } is not universal. Two more top-level audit keys
exist and aren't shown below: extendedConfig and temporal, both {findings, total}-shaped.
Every finding object embeds its compliance mapping:
{
"type": "NTLMV1_ALLOWED",
"severity": "high",
"category": "advanced",
"title": "NTLMv1 Authentication Allowed",
"description": "...",
"count": 1,
"compliance": [
{"framework": "ANSSI_PA099", "control": "R22"},
{"framework": "RGPD", "control": "art.32(1)(a)"},
{"framework": "NIS2_FR", "control": "Art.21(2)(h)"},
{"framework": "CIS_v8", "control": "§2.3"}
],
"affectedEntities": []
}On error, the document includes a structured code that matches one of the 24 LDAP error codes documented in docs/configuration/ad-troubleshooting.md:
{
"success": false,
"error": {
"code": "LDAP_TLS_IP_SAN_MISSING",
"message": "LDAP URL uses an IP address but the certificate has no IP SAN",
"resolution": "Use the DC FQDN listed in the certificate SAN.",
"raw": "tls: failed to verify certificate: x509: cannot validate certificate for 10.0.0.10 because it doesn't contain any IP SANs"
}
}When running in server mode, ETC Collector exposes a REST JSON API at http://<host>:8443/api/v1/ (or https:// once server.tls.cert/server.tls.key are configured — with no TLS configured, the server binds plain HTTP, confirmed live on v3.2.0). GUI-token auth to mint a JWT, then JWT bearer auth for everything else. Where the GUI token itself comes from, the login screen, and when TLS becomes mandatory (--host beyond loopback): docs/configuration/server-login.md.
HOST=http://localhost:8443
GUI_TOKEN="etcsec_gt_..." # shown once at install time, or: etc-collector gui-token reset
# 1. Mint a JWT from the GUI access token
JWT=$(curl -s -X POST $HOST/api/v1/auth/token \
-H "Content-Type: application/json" \
-H "X-GUI-Token: $GUI_TOKEN" \
-d '{"service":"automation","duration":"24h"}' | jq -r .token)
# 2. Trigger an async audit
JOB_ID=$(curl -s -X POST $HOST/api/v1/audit/ad \
-H "Authorization: Bearer $JWT" \
-d '{"async": true}' | jq -r .jobId)
# 3. Poll job status — the result is embedded in this same response once status is "completed"
curl -s $HOST/api/v1/audit/jobs/$JOB_ID -H "Authorization: Bearer $JWT" | jq '{status, result}' > result.json
# 4. Liveness probe (no auth)
curl -s $HOST/health→ Full endpoint reference: docs/API.md
Requires Go 1.26+ (matches go 1.26.7 in go.mod).
git clone https://github.com/etcsec-com/etc-collector-com.git
cd etc-collector-com
# Build
make build
# OR manually
go build -o etc-collector ./cmd/etc-collector/
# Cross-compile for every supported target
mkdir -p dist
for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64; do
OS=${target%/*}; ARCH=${target#*/}
EXT=""; [ "$OS" = "windows" ] && EXT=".exe"
GOOS=$OS GOARCH=$ARCH CGO_ENABLED=0 go build \
-ldflags="-s -w" \
-o dist/etc-collector-$OS-$ARCH$EXT \
./cmd/etc-collector/
doneBuild flags:
CGO_ENABLED=0— pure-Go static binary (no glibc dependency)-ldflags="-s -w"— strip debug info, ~30% smaller binary
| Topic | File |
|---|---|
| Getting started — Active Directory | docs/configuration/ad-getting-started.md |
| Getting started — Azure / Entra ID | docs/configuration/azure-getting-started.md |
Server/GUI login (access token, --host/TLS) |
docs/configuration/server-login.md |
| AD connection modes (LDAP/LDAPS/StartTLS) | docs/configuration/ad-connection-modes.md |
| AD TLS certificate extraction (5 methods) | docs/configuration/ad-tls-certificates.md |
| AD troubleshooting runbook (24 error codes) | docs/configuration/ad-troubleshooting.md |
| Audit scope (categories / IDs / profiles) | docs/configuration/audit-scope.md |
| Compliance frameworks (mapping table) | docs/configuration/compliance.md |
| Permissions (AD account & Azure app setup) | docs/configuration/permissions.md |
| Configuration reference | docs/configuration/ |
| AD vulnerability catalog | docs/vulnerabilities/active-directory/AD_VULNERABILITY_CATALOG.md |
| Azure vulnerability catalog | docs/vulnerabilities/azure/AZURE_VULNERABILITY_CATALOG.md |
| Operating modes (Standalone vs SaaS daemon) | docs/modes/ |
| Features overview | docs/features/ |
| REST API reference | docs/API.md |
| Licensing | docs/LICENSING.md |
One binary, one license — since v3.2.0 there is no Community/Pro split. AD audit, Microsoft Entra ID audit, ADCS (ESC1–6, ESC8–11), attack-paths, Azure Risk Protection, the REST API, the embedded GUI, standalone server mode and SaaS daemon mode all ship in the same download.
ETC Collector is licensed under the Functional Source License, Version 1.1, Apache 2.0 Future License (FSL-1.1-ALv2). You are free to use, modify, self-host and run this software in production — including commercial and enterprise use — but not to build a product or service that competes with ETC Collector. Each release automatically converts to the permissive Apache License 2.0 two years after its publication date. See the LICENSE file for full terms, docs/LICENSING.md for more detail, and fsl.software for the official license text.
If you are unsure whether your intended use is a Competing Use under the license, contact support@etcsec.com.
- Documentation — etcsec.com
- Issues — github.com/etcsec-com/etc-collector-com/issues for bug reports and feature requests
- Security —
security@etcsec.com(responsible disclosure, 48-hour acknowledgment) - Commercial / Pro / SaaS —
support@etcsec.com
{ "success": true, "provider": "ad", "audit": { "metadata": { "provider": "ad", "domain": { "name": "example", "baseDN": "DC=example,DC=com", "ldapUrl": "ldaps://dc01.example.com:636" }, "options": { "includeDetails": true, "includeComputers": true, "includeConfig": true }, "execution": { "timestamp": "2026-04-22T20:21:00Z", "duration": "1.08s" } }, "summary": { "objects": { "users": 546, "users_enabled": 27, "users_disabled": 519, "groups": 154, "ous": 352, "computers": 100 }, "risk": { "score": 33, "rating": "high", "findings": { "critical": 3, "high": 40, "medium": 51, "low": 20, "info": 0, "total": 114, "totalInstances": 270, "records": 270 } }, "complianceScores": [ { "framework": "ANSSI_PA099", "score": 40.4, "rating": "high", "controlsTotal": 52, "controlsPassed": 21, "controlsFailed": 31, "controlsManual": 37, "failedControls": ["R1", "R2", "R6", "R22", "R28"], "maturityAxes": [ {"name": "Politique de mot de passe", "level": 3, "coverage": 0.6}, {"name": "Comptes privilégiés", "level": 2, "coverage": 0.4}, {"name": "Authentification", "level": 4, "coverage": 0.8}, {"name": "Délégation", "level": 1, "coverage": 0.2}, {"name": "Supervision & audit", "level": 3, "coverage": 0.6} ] } ] }, "accounts": { "findings": [/* ... */] }, "computers": { "findings": [/* ... */] }, "groups": { "findings": [/* ... */] }, "permissions": { "findings": [/* ... */] }, "adcs": { "findings": [/* ... */] }, "gpoSecurity": { "findings": [/* ... */] }, "trustsAnalysis":{ "findings": [/* ... */] }, "domainConfig": { "domainInfo": {/* ... */}, "passwordPolicy": {/* ... */}, "kerberosPolicy": {/* ... */} }, "security": { "passwords": {/* ... */}, "kerberos": {/* ... */}, "advanced": {/* ... */} }, "attackGraph": { "domain": "example", "generatedAt": "2026-04-22T20:21:00Z", "version": "1.0", "targets": [/* ... */], "uniqueNodes": [/* array of {id,name,type,pathCount,sid} */], "stats": { "totalPaths": 58, "byRisk": {/* ... */}, "byType": {/* ... */}, "byTarget": {/* ... */} }, "paths": [{ "id": "path-001", "risk": "critical", "type": "DCSYNC", "hops": 1, "chain": [/* ... */] }] }, "extendedConfig":{ "findings": [/* ... */], "total": 0 }, "temporal": { "findings": [/* ... */], "total": 0 } } }