Files
ADconsle/docs/active-directory.md
T
kzbkowskiandClaude Sonnet 5 1126d96271
CI / PHP 8.2 (push) Failing after 2m46s
CI / PHP 8.3 (push) Failing after 59s
CI / PHP 8.4 (push) Failing after 1m7s
ADconsole v1.0.0
First public release.

- docs: document the audit log, user offboarding, CSV export and the
  "require a reason for destructive actions" setting across README, docs/
  and CLAUDE/AGENTS.md; note retention for panel_audit
- docs: fill the publishing placeholders (repo URL, security contact,
  issue-template links) and add a semantic-versioning policy
- move agent/contributor context into CLAUDE/ (AGENTS.md + a README);
  root CLAUDE.md is now a thin @-import pointer
- add a favicon (shield-lock, matching the login screen) to the login
  page and the app layout
- CHANGELOG: promote [Unreleased] to [1.0.0]; keep the earlier internal
  build as [0.1.0]

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfuvfwexztdS7LDpGuKsuK
2026-09-08 23:14:39 +02:00

5.2 KiB

Active Directory setup

ADconsole needs three things from your directory: a service account to bind with, an encrypted channel (LDAPS/STARTTLS), and a login group whose members may use the panel. What the panel can actually change is governed entirely by AD delegation on that service account — grant only what you need.

1. Service account

Create a dedicated account, e.g. svc-adconsole:

  • Password never expires, cannot change password, not a member of any privileged group by default.
  • Deny interactive/RDP logon (it only needs "Access this computer from the network" to the DCs, i.e. LDAP bind).
  • Put its DN in LDAP_USERNAME and its password in LDAP_PASSWORD.

Test the bind:

php artisan tinker
>>> \LdapRecord\Container::getConnection('default')->connect();  // no exception = OK

2. Encrypted transport (LDAPS / STARTTLS)

AD refuses password writes (unicodePwd) and other sensitive changes over an unencrypted connection, so this is mandatory for anything beyond read-only use.

  • LDAPS (recommended): ensure the DCs have a certificate with Server Authentication EKU (AD CS "Domain Controller" / "Kerberos Authentication" template auto-enrols this). Set LDAP_SSL=true, LDAP_PORT=636.
  • STARTTLS: LDAP_TLS=true, LDAP_PORT=389.
  • If the app host does not trust the issuing CA, set LDAP_TLS_VERIFY=false (skips chain validation — acceptable on a trusted network, not ideal). To verify instead, install the internal root CA in the app host's trust store (/etc/ssl/certs, update-ca-certificates) and set LDAP_TLS_VERIFY=true.

Quick check from the app host:

openssl s_client -connect dc01.corp.example.com:636 -showcerts </dev/null

3. Base DN and OU layout

LDAP_BASE_DN is used by every module. If your users, computers and groups live in sibling OUs, the base DN must be an ancestor of all three — normally the domain root:

LDAP_BASE_DN="DC=corp,DC=example,DC=com"
LDAP_DEFAULT_USER_OU="OU=Staff,DC=corp,DC=example,DC=com"
LDAP_DEFAULT_COMPUTER_OU="OU=Workstations,DC=corp,DC=example,DC=com"
LDAP_DEFAULT_GROUPS_OU="OU=Security Groups,DC=corp,DC=example,DC=com"

A too-narrow base DN is the #1 cause of "the computers / groups list is empty".

4. Login group

AD_LOGIN_GROUP = the group whose members may sign in. Use the full DN:

AD_LOGIN_GROUP="CN=ADconsole Admins,OU=Security Groups,DC=corp,DC=example,DC=com"

Nested membership and the primary group are honoured (App\Ldap\Rules\MemberOfLoginGroup). Empty value = nobody can log in. This is an application gate only — it grants no directory rights.

5. Delegation — grant per feature

Delegate on the OUs you want ADconsole to manage (Delegation of Control Wizard, or dsacls). Map the features you'll use to the rights below and skip the rest.

Feature Rights on the target OU(s)
Read lists, search, view profiles Read all properties on user/computer/group objects (Authenticated Users usually have this already)
Reset password Reset Password extended right on user objects
Force "change at next logon" Write pwdLastSet
Unlock account Write lockoutTime
Enable / disable account Write userAccountControl
Password-expiry toggle (never expires) Write userAccountControl
Account expiry date Write accountExpires
Create user Create/Delete User objects; Write the attributes your panel writes (name, sAMAccountName, userPrincipalName, mail, …)
Delete user / computer / group Delete on that object class
Move between OUs Write + Delete Subtree on source, Create child on destination
Group membership add/remove Write member on the group objects
Edit group description / mail Write description, mail
Create group Create Group objects + write its attributes
Reset computer trust account Reset Password extended right on computer objects
Photo upload Write thumbnailPhoto
LAPS password (read) Read ms-Mcs-AdmPwd / msLAPS-Password (+ …-EncryptedPassword). These are not readable by default — grant explicitly, e.g. Set-AdmPwdReadPasswordPermission / Windows LAPS ACL.
BitLocker recovery keys (read) Read msFVE-RecoveryInformation child objects (msFVE-RecoveryPassword)
GPO module Read on CN=Policies,CN=System,<baseDN> and read gPLink on OUs (default for Authenticated Users). No write is ever attempted.

Example — password reset + unlock + enable/disable on one OU:

dsacls "OU=Staff,DC=corp,DC=example,DC=com" /I:S `
  /G "CORP\svc-adconsole:CA;Reset Password;user" `
  "CORP\svc-adconsole:WP;lockoutTime;user" `
  "CORP\svc-adconsole:WP;userAccountControl;user" `
  "CORP\svc-adconsole:WP;pwdLastSet;user"

6. Verify

  1. Sign in as a member of AD_LOGIN_GROUP.
  2. Open Users — the list and OU tree populate.
  3. Open a test account → Reset password → set a value → confirm on a real logon. A failure here almost always means "no encrypted channel" or "Reset Password right not delegated".
  4. Repeat for the other features you delegated.

See troubleshooting.md for specific errors.