Files
ADconsle/docs/active-directory.md
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

119 lines
5.2 KiB
Markdown

# 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:
```sh
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:
```sh
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:
```powershell
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](troubleshooting.md) for specific errors.