Skip to content

LAPS: local administrator passwords

Steward lets a delegated helpdesk operator look up a computer in the OUs they manage and reveal its LAPS password (the local administrator password that Windows LAPS rotates on each machine), once, with a ticket and a step-up, audited. It never shows the password of a domain controller or a Tier 0 server, and the console process never holds the account that can read it.

LAPS runs in the password service, so it needs the helpdesk.

What operators can do

Page / action Permission (delegation) What happens
Computers page: search or list, read-only computers.read Name, DNS name, OS, last sign-in (lastLogonTimestamp, up to ~14 days late by design), enabled, LAPS expiry. Never a password.
Computer detail > Show LAPS password computers.laps.read Ticket (always), step-up (always), confirmation; the password service reads it with the LAPS account; shown once with a copy button, hidden after 30 seconds.
Computer detail > Expire now computers.laps.expire Sets msLAPS-PasswordExpirationTime to now; the device sets a new password at its next policy cycle (typically within the hour, or at once with Invoke-LapsPolicyProcessing).

Like every directory action, the three are granted per delegation (scope = where the computers live), and every request is decided on fresh facts:

  • Never manageable, whatever the role (Super administrator included): domain controllers, read-only DCs, machines trusted for unconstrained delegation or for protocol transition, any computer that is a member (even nested, or through its primary group) of a sensitive group (put your Tier 0 servers in one), adminCount=1, any computer below a Tier 0/1 OU, and anything that is not a computer account. A missing signal is a refusal. These computers are not even listed.
  • Protected computers: a computer that is a member of a protected (VIP) group (Configuration > Protection) can be listed with computers.read, but revealing or expiring its password needs vip.manage in the same delegation. To protect a set of machines, create a group (for example GG-Protected-Computers), put the computer accounts in it and add it to the protected groups.
  • computers.laps.read and computers.laps.expire require computers.read in the same delegation (the role editor refuses otherwise: computers_read_required).

Set it up

1. Windows LAPS on the domain

Windows LAPS is built into Windows Server 2019+ and Windows 10/11 (April 2023 update). On a domain without its schema, a Schema Admin runs, on a DC or a machine with the LAPS PowerShell module:

Import-Module LAPS
Update-LapsADSchema                                       # adds the msLAPS-* attributes (forest-wide, irreversible like any schema change)
Set-LapsADComputerSelfPermission -Identity "OU=Workstations,OU=Corp,DC=corp,DC=example,DC=ch"   # lets the computers write their own password

Then a GPO (Computer Configuration > Administrative Templates > System > LAPS) with Configure password backup directory = Active Directory, and Enable password encryption = Disabled for the OUs the helpdesk serves (see encrypted passwords). Check on a client: Invoke-LapsPolicyProcessing, then Get-LapsADPassword -Identity PC01 -AsPlainText as a domain admin.

2. A dedicated LAPS account

Create a plain user, for example svc-steward-laps, member of no administrative group, and never the read, write or password-reset account (the installer and the delegation script refuse that).

3. Give its password to the password service only

The guided install asks for it after the helpdesk ("Set up the LAPS identity now?", then the UPN and the password, typed hidden). With options (the helpdesk, and so the password-reset account, is required):

sudo install -m 600 /dev/null /root/laps.txt && sudo vi /root/laps.txt      # the password, one line
sudo deploy/install.sh --name acme --hostname steward.acme-client.ch --helpdesk \
     --laps-upn svc-steward-laps@corp.example.ch --laps-password-file /root/laps.txt
sudo shred -u /root/laps.txt

The script stores the password in <instance>/secrets/ad-laps-read (0600, owned by the container user) and writes the .env; re-runs keep it. The reset account and its password are kept from the first helpdesk install.

Without the installer: write the password to a file (for example secrets/ad-laps-read, chmod 600), set in the instance's .env, then docker compose up -d:

STEWARD_LAPS_UPN=svc-steward-laps@corp.example.ch
STEWARD_LAPS_PASSWORD_FILE=./secrets/ad-laps-read

deploy/compose.yml passes the UPN to the console (Helpdesk:LapsIdentityUpn: Configuration page, diagnostics, export) and the UPN plus the password file to steward-pwd only (PasswordService:LapsIdentity). The console never sees the password, and it cannot be entered in the web UI (Configuration shows the account read-only).

4. Grant the delegations

In Delegations, give computers.read, computers.laps.read and, if wanted, computers.laps.expire on the OUs that hold the workstations.

5. Apply the AD rights

Configuration > Diagnostics > Export AD delegation, then a domain administrator applies it (Delegation model and AD rights). For each LAPS delegation it grants the LAPS account, inherited by computer objects only:

  • Read property + Control access on msLAPS-Password (and ms-Mcs-AdmPwd when the legacy schema exists);
  • for computers.laps.expire: Write property on msLAPS-PasswordExpirationTime (and ms-Mcs-AdmPwdExpirationTime).

This is the same right as Set-LapsADReadPasswordPermission (Windows LAPS module), restricted to the clear-text attribute. The script refuses any other LAPS combination (another account, another object class, msLAPS-EncryptedPassword, any other attribute).

6. Check the diagnostics

Configuration > Diagnostics, LAPS section:

Check Meaning
laps_schema_windows / laps_schema_legacy_only / laps_schema_missing Which LAPS schema the directory has
laps_identity_upn_missing STEWARD_LAPS_UPN is not set on the console
laps_identity_not_configured / laps_bind_failed / laps_identity_ok The password service holds the account / it signs in
laps_service_unreachable The console cannot reach the password service
laps_read_rights_effective / laps_read_rights_missing On a sample LAPS-managed computer of each scope, AD returns the password attribute to the LAPS account. Checked with an attribute-names-only read: diagnostics never fetch the password
laps_encrypted_only The sample stores only an encrypted password (not supported)
laps_expire_rights_present / laps_expire_rights_missing The LAPS account may write the expiration time (allowedAttributesEffective)
no_sample_computer No LAPS-managed standard computer in that scope yet: rights cannot be verified
laps_needs_password_service A delegation names a LAPS action but the helpdesk (password service) is off

What protects a LAPS read

  1. A ticket is always required (400 ticket_required), even when the instance does not require tickets for other changes.
  2. One audited decision (authz.decision, computers.laps.read) on fresh facts read by GUID.
  3. A step-up is always required, even when the instance step-up mode is none: an AD operator proves it with an authenticator code if enrolled, otherwise their AD password (audited auth.reauth); in the modes authenticator and password it is the usual step-up. Teleport operators: their Teleport session is the step-up, as for every change (Authentication:Teleport:StepUp: Deny refuses them). When refused, the API answers 403 strict_reauth_required with the proof to ask for.
  4. An explicit confirmation, then the console asks the password service over the signed internal link. The service checks again: computer under a managed OU, at the DN the decision was made on, classified standard, at most 5 reads or expiries per computer per 10 minutes (PasswordService:MaxLapsReadsPerTarget, PasswordService:RateWindow).
  5. The LAPS account reads msLAPS-Password (Windows LAPS), else ms-Mcs-AdmPwd (legacy LAPS, read-only support). AD itself is the last check: without the Control access right the confidential attribute is simply not returned (the console then says "no readable LAPS password").
  6. The password goes back to the console encrypted for it (AES-256-GCM with the link key, bound to the computer and to the direction), then to the browser once. It is never stored, logged or audited: the audit row computers.laps.read records who, which computer, the ticket, the outcome and the source (source:windows / source:legacy). The password service logs laps_operation lines (operation, outcome, computer, operator, correlation id), never the password.
  7. In the browser the password is never cached (no query cache, no storage, no URL), is shown with a copy button and hidden after 30 seconds; closing the dialog clears it.

Encrypted passwords are not supported

Windows LAPS can encrypt the password in AD (msLAPS-EncryptedPassword, DPAPI-NG, decryptable only by an authorized principal on a domain-joined Windows machine). Steward does not decrypt it and never grants the right to read it: a computer that stores only an encrypted password answers 409 laps_encrypted_unsupported, and the diagnostics warn (laps_encrypted_only). Use clear-text mode for the OUs the helpdesk serves (ADPasswordEncryptionEnabled = False in the LAPS policy), or keep those computers out of Steward.

API

Endpoint Notes
GET /api/computers?q=&ou= computers.read; without q it lists the operator's scope (bounded, truncated when cut)
GET /api/computers/{id} computers.read; 404 when not readable (no existence oracle); actions = display hints
POST /api/computers/{id}/laps { confirm, ticket } 200 { account, password, updatedUtc, expiresUtc, source }; 403 strict_reauth_required; 409 laps_no_password / laps_encrypted_unsupported / moved; 429 rate_limited; 503 laps_not_configured / laps_unavailable
POST /api/computers/{id}/laps/expire { confirm, ticket } 200 { expired: true }; 409 laps_not_managed; 403 ad_denied

The LAPS endpoints exist only when the password service is deployed (Helpdesk:Enabled), like the other helpdesk endpoints.