Skip to content

[FEATURE] A --harden-files verb: the service knows the correct ACL and cannot apply it, and only the installer ever does #2352

Closed

Description

@erikdarlingdata

The service computes the correct ACL, detects when the real one is wrong, and prints the remediation — but the only thing that ever applies it to an existing install is install-darling.ps1. A box set up any other way, or whose ACLs drifted, leaves the operator hand-typing three icacls lines out of a log message.

Seen on a real box today

The PostgreSQL monitoring host was created with sc create directly rather than through install-darling.ps1, so the installer's section 4b hardening never ran. Every service start since has logged this, correctly:

[ERROR] Could not restrict the ACL on C:\PerformanceMonitorDarling\darling.json
        (owner is BUILTIN\Administrators; readable by BUILTIN\Users)
        (Attempted to perform an unauthorized operation.)
        ... From an ELEVATED prompt: icacls "..." /inheritance:d  then  /remove:g "BUILTIN\Users"
        then  /grant "NT SERVICE\PerformanceMonitor Darling:(F)"
[CRIT ] C:\PerformanceMonitorDarling\darling.json is READABLE by Users/Authenticated Users/Everyone.
        It holds every monitored server's encrypted password plus the MCP and web access tokens...

Confirmed against the file itself: BUILTIN\Users: ReadAndExecute present, AreAccessRulesProtected = False, owner BUILTIN\Administrators. Same on C:\ProgramData\PerformanceMonitorDarling and the credential files under it.

The diagnostic half is genuinely good and I do not want to change it. It named the file, the owner, the exposure, why a restart will not help, and the exact commands. DarlingWorker.TryHardenConfigFile's comment explains the reasoning — that a log line saying the ACL is wrong is not the same as knowing what to type — and it is right.

The gap

DarlingFileSecurity.HardenFile already encodes the correct DACL. IsReadableByOrdinaryUsers already verifies the result. The service cannot call them successfully because a virtual service account has neither WRITE_DAC on a file it does not own nor the privilege to take ownership — as the log says, and as the code comments already anticipated.

So the capability exists and the authority does not, and nothing lets an operator who has the authority invoke it. TryHardenExportedSecret (DarlingCliCommands.cs:1174) is the closest thing and it only covers a freshly exported file.

Proposal

PerformanceMonitor.Darling.Service.exe --harden-files, run from an elevated prompt:

  • Hardens darling.json (allowInteractiveRead: true — the Viewer and CLI verbs read it as the interactive operator), its darling.json.bak-* siblings (false), the ProgramData directory, and the credential files under it (false)
  • Verifies and reports per file rather than claiming success. "We tried" is not "the secret is not readable" — a distinction TryHardenExportedSecret's own doc comment already makes
  • Exit code reflects whether anything is still readable by ordinary users, so it is usable in a provisioning script
  • Idempotent, so it is safe on a healthy box and can just live in a runbook

That is mostly wiring: the DACL construction, the verification and the file list all exist. install-darling.ps1 section 4b then becomes a caller rather than a second implementation of the rule — the same anti-drift move #2343 made for the install-location check, which found real bugs in the PowerShell/C# pair precisely because they had been written twice.

Also worth considering

The install-location diagnostic from #2343 fires on an unreadable tree. This is the opposite failure — a tree readable by too many — and it has no equivalent startup verdict beyond the per-file CRITICAL. Both are "the install is in a place that hurts you"; they may deserve one report.

Not urgent for 3.5.0

No customer is blocked: the installer does the right thing on the documented path, and the log tells anyone off that path exactly what to type. This removes a manual step and a chance to typo an ACL, which is worth doing but not worth holding a release for.

Activity

  1. erikdarlingdata commented on Aug 19, 2026

    @erikdarlingdata
    OwnerAuthor

    Shipped in #2358. --harden-files re-applies the ACLs from an elevated prompt, verifies every target afterwards rather than claiming success, and exits non-zero if anything is still readable by ordinary users — so it can gate a provisioning script.

    darling.json is the only target that keeps an INTERACTIVE read ACE (the Viewer and CLI verbs run as the operator and must read it); the .bak-* siblings, the store directory and the DPAPI credentials get none, because nothing reads a backup.

    Two deliberate softnesses: a config that will not load is a warning rather than a stop — an unreadable darling.json is the failure this exists to repair, so refusing to run without it would refuse exactly when it is needed — and missing targets are skipped quietly, since a BYO-Postgres install has no managed credential files.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions