41. The hardening planner: priority, breakage, guards and staged enforcement
This guide describes Lawliet 1.5.14.s (the current release). Screens, commands and procedures may change in later versions; the usage guide is updated with each release. Check the version your server runs under Settings > Platform or with ./lawliet version, and see the Releases page for what changed.
The planner answers four questions for one host or a group of hosts, about the catalog controls that fail there:
- In what order should they be fixed (prerequisites first, conflicts resolved)?
- Why that order (a priority with every reason listed)?
- What will each change break (who still uses what it removes)?
- What must be fixed first so the change does not cut Lawliet's own access or a workload (the guards)?
It then lets you stage the audit step of controls that must be audited before they are enforced, and request the enforce. Every enforce request becomes a hardening run that waits for a second administrator on the Hardening page (chapter 26).
Path: Hardening > Planner (the Planner button at the top of the Hardening page).
Permissions:
| Task | Permission | Four-eyes |
|---|---|---|
| Build a plan for a host or a group | View hardening | - |
| Stage an audit step | Run hardening | no (it changes only auditing on the host, see 41.5) |
| Cancel an audit step (API) | Run hardening | no |
| Accept the log volume of high-volume audit settings (API) | Run hardening | yes - "Execute remediation" (always a second person) |
| Request an enforce | Run hardening | no for the request; approving the run is yes - "Execute remediation" |
| Override the do-not-disrupt baseline or an "unknown" Windows guard | Run hardening | yes - "Execute remediation", and only when a second approver really exists |
| Accept a lockout risk (typed acknowledgement) | Run hardening | the run then needs someone other than the requester to approve it |
41.1 Procedure: build a plan#
- Open Hardening and press Planner.
- In Filter hosts, type part of a host name. Click one host, or several (at most 50).
- Choose the Window - last 7 days, last 14 days, last 30 days or last 90 days - the period of collected usage the breakage prediction reads for this view. (An enforce request always reads at least the last 30 days, whatever you choose here.)
- Choose the Profile: strict (every control of the platform, the default) or standard (40.2).
- Press Plan host (one host) or Plan N hosts.
What you see for one host:
- The header: host name, platform / role / OS version, domain, agent online or agent offline, internet-facing when the asset is marked so, and AD collection N: M path(s) or no AD collection.
- Six tiles: Failing (catalog controls failing on the host's latest scan), Planned, Ready (can be requested now), Audit first (audit step not complete), Will break (usage seen), Guarded (a guard blocks).
- Order of work (N steps): open it to read every step in order - its number, kind (audit, enforce, manual), control id, and "after <ids>" for what must come first.
- One card per control (41.2).
- At the bottom: "N not applicable, M not in the catalog" - open it to see why each control was left out (another role or version, or a legacy control without a catalog record).
What you see for several hosts: "N hosts, M failing controls, K ready (first 50 hosts). Highest priority first; open a host to stage or request." and a table Score, Host, Control, Breakage, Ready (the first 200 rows). Click a host name to plan that host alone; staging and requesting are done per host.
If it fails:
| What you see | Cause | Fix |
|---|---|---|
| "Nothing to plan" - "No failing catalog control on this host's latest scan." | The host has no failing catalog control, or was never scanned against a catalog profile | Scan it against its profile (22.2, framework lawliet_<platform>_<role>) |
| "No agents." | No host matches the filter, or none is enrolled | Clear the filter |
| A red line with a message | The plan could not be built (for example too many group plans in a minute: at most 6 per user) | Wait a minute and plan again |
41.2 Reading a control card#
Each card shows its position in the order (#N), the title, the id, the severity and chips:
| Chip | Meaning |
|---|---|
| priority N | The control's priority, 0 to 100 (41.3) |
| audit first / audit complete | An audit_then_enforce control whose audit step has not / has finished |
| manual | A report-only control: nothing to enforce; its card cannot be selected |
| Will break | Usage of what the change removes was seen in the window (red) |
| No use seen / No use seen (low confidence) | Every signal collected and quiet; low confidence under 7 covered days, or a sampled signal |
| Breakage unknown | Something needed for the prediction is missing (41.3) |
| Not predicted | The control names no usage signal; read its impact text |
| guard blocks | A guard does not pass (41.4) |
| baseline warning | The do-not-disrupt baseline would be disrupted, but the host is in warn mode (chapter 42) |
| ready to request | Nothing blocks a request now |
"Blocked: ..." under the chips lists everything that stops an enforce now.
Press the arrow on the right to open the details:
- Why this priority (N): each reason with its points.
- Predicted breakage: the plain sentence ("will break: these clients/accounts/apps used it in the last N days", "no use seen", or why it is unknown), the first ten subjects seen (
label - Nx, last <time>), and for each signal that is not collected, the audit setting to "turn on". - Guards: each guard with a shield (pass, fail, or a question mark for unknown), its name and its explanation; then the numbered fix order.
- Attack-path edges it cuts: each attack technique the control removes, with the number of AD attack paths and critical targets in the domain's latest collection that use it, or "not modelled in the AD graph".
- Why:, Impact: and Requires: from the catalog.
41.3 Priority and breakage#
Priority#
priority = severity + attack-path impact + exposure - breakage (kept between 0 and 100)| Factor | Points |
|---|---|
| Severity | critical 40, high 30, medium 20, low 10 |
| Attack-path impact (at most 40) | Per attack technique the control cuts (NTLM relay to LDAP or SMB, coerced authentication, credential theft, local admin reuse, Kerberoasting, machine-account abuse, delegation abuse, DCSync, ACL takeover, name poisoning, legacy protocol exploits, cleartext remote management, SSH credential attacks, local privilege escalation) its base points; where the AD graph models the technique, plus up to 10 for the paths using it, up to 9 for critical targets reached and up to 6 for open AD findings |
| Exposure (at most 25) | internet-facing 15; domain controller 15 (else a tier0 asset tag 10); asset criticality critical 10, high 5. "No asset profile for this host: exposure unknown" when the host is not classified (38.1) |
| Breakage | -10 when the change is predicted to break something now: the control is ranked lower, never hidden, and the reason says to move the named clients off first |
A prerequisite inherits the priority of what it unlocks, so it is ordered before it. Two failing controls that conflict: the higher-priority one stays and the other is blocked. A dependency cycle is printed (A -> B -> A) and blocked, with everything that waits on it.
Breakage prediction#
The planner reads the usage signals the control names (for example NTLM authentications, SMB1 sessions, LDAP binds, TLS handshakes, SSH logins by method) from what the agents on the host collected (chapter 45).
| Verdict | Meaning |
|---|---|
| Will break | These clients, accounts or programs used what the change removes in the window |
| No use seen | Every signal was collected and quiet over the window |
| No use seen (low confidence) | Quiet, but collected for fewer than 7 days, or the signal is sampled (socket and process samples, SMB sessions every 10 minutes, ticket caches hourly, devices present, Office files in local folders) |
| Breakage unknown | See below |
| Not predicted | The control names no signal |
What "unknown" means. Silence counts as "nobody uses it" only when Lawliet knows the signal was being collected. The prediction is unknown when:
- the signal is not collected yet on that host (no agent 2.1.10.s, the signal switched off, or no coverage marker received);
- collection stopped (the newest coverage report is older than 48 hours);
- the audit setting the signal needs is off on the host (
audit_off): the card names the setting to turn on, and staging the audit step switches it on (41.5); - the host's log wrapped past what the agent had read, the agent fell behind, or the server dropped rows over its budget - "use cannot be ruled out";
- the signal cannot be observed by an agent at all (for example whether a driver is compatible with memory integrity): the evidence to look at instead is named;
- for a change written to a domain Group Policy: a domain controller of the domain has no agent, or there is no AD collection - every DC is checked, and the least covered DC counts;
- the log store could not be read.
Unknown is never read as "no use". An enforce whose breakage is predicted or unknown needs an explicit acknowledgement with a justification (41.6).
41.4 Guards: what must be true before an enforce#
A guard is a named safety check on the host's facts, run before any enforce: when the plan is built, when the enforce is requested, and again when the run is approved. Each answers:
| Answer | Meaning | What you can do |
|---|---|---|
| pass | Shown to be safe, with the evidence named | Nothing |
| fail | A measured reason the change would break something | Fix the cause, in the fix order shown, then request again. A fail is never overridable |
| unknown | The data the guard needs is missing, stale, cut short or unreadable | Blocks like a fail (fail closed). Collect the data (the explanation says what), or - for Windows guards and the baseline guard only - override with a second administrator (41.7) |
What makes a Windows guard unknown: an agent snapshot older than 26 hours, event telemetry not reported for 48 hours, a host inventory older than 26 hours, an AD collection older than 7 days, fewer than 7 days of collection, or data that was truncated, partial, timed out, lagging or unreadable. History-based guards (network profiles, devices, WINS and DNS counters) count only measured time: 90% of the last 7 days.
Lawliet's own access (every platform)#
| Guard | Passes when |
|---|---|
no_lawliet_http_credential_for_host | No Lawliet deployment credential reaches the host over WinRM HTTP 5985 |
no_lawliet_ntlm_credential_for_host | No Lawliet WinRM credential logs in with NTLM or may fall back to it (a local account, auth mode NTLM, or automatic without Kerberos proven) |
https_listener_present | The latest scan shows the WinRM HTTPS listener control passing |
lawliet_ssh_access_preserved | No Lawliet SSH credential is refused by the change (password logins, root logins) |
agent_port_open | The agent reported in the last 10 minutes, so it can validate (and roll back) a firewall change |
not_last_admin_path | At least one management path Lawliet knows survives: the online agent, or a credential the change does not affect |
dc_count_ge_2_for_dc_changes | The host is not a domain controller, or its domain has at least two |
protect_host_baseline | The change leaves the host's approved do-not-disrupt baseline intact (chapter 42). Added automatically to every control that declares an effect |
Typical fix orders, as the console prints them: "Add the WinRM HTTPS listener on 5986 (WIN-WINRM-HTTPS-LISTENER) if the host has none." - "Switch credential '<name>' to HTTPS 5986 and run Test connection against <host>." - "Set credential '<name>' to Kerberos with a domain account and the host's DNS name ..." - "Request the enforce of <control> again."
A change that breaks how Lawliet reaches the host is also refused by the Fix plan route with "Breaks Lawliet access: <what breaks> First: <what to change>" (22.4).
Windows guards#
Evaluated from the host's own agent (Windows guard snapshots once an hour, user-right holders, SMB client dialects, event telemetry, the host inventory), the latest AD collection and Lawliet's deployment history. A change written to a domain Group Policy is judged on every domain controller of the domain, each through its own agent; a DC without an agent is unknown. Which agent speaks for a DC: the agent whose host name is that domain controller's and whose AD computer SID is the DC's computer object in the AD collection, with the freshest heartbeat, and only when its own snapshot shows it is a DC. A Windows agent reports the SID of its own computer account (agent 2.1.10.s and later); the server keeps the first one it hears from each agent and never takes a different one. The DC is unknown, with the reason in the guard text, when its agent has not reported a SID yet (an older agent: upgrade it), reports another computer's SID, has reported a changed SID (a review item Agent reported a different AD computer SID, high on a DC or tier0 asset, medium elsewhere; enrol the agent again if the computer was re-joined to the domain), when a live agent under another host name claims the same SID, or when two live agents claim the same DC. The SID is still the agent's own report and is readable by any domain user: the binding stops host-name reuse, stale or re-created DC records and a changed claim, not an enrolled agent that lies from the start about a DC that runs no agent of its own.
Laptops and hosts that are often off. Gaps in collection are never treated as quiet: the network-profile guard (lawliet_containment_rules_survive) and the Thunderbolt / DMA guards (no_thunderbolt_network_or_dock_in_use, kernel_dma_protection_state) need 90% measured coverage of the last 7 days. A laptop that is powered off or offline for long stretches will usually not reach it, so these guards stay unknown there. Expect to use the four-eyes override (41.7) for those controls on laptops, after checking the host by hand (does it join public networks, does it use a Thunderbolt dock).
| Guard | Passes when |
|---|---|
dc_replication_healthy | On every DC all inbound replication links are healthy (no failures, last success within 24 h) and SYSVOL is replicated by DFSR in the normal state |
krbtgt_reset_replication_converged | Every DC holds the same krbtgt password time and replicates healthily |
rights_holders_not_in_use_by_services | No service, scheduled task, batch or service logon, service SID, IIS app pool or group member loses a user right the change removes |
lawliet_management_path_preserved | Every Lawliet path survives: the agent's outbound channel, and each WinRM credential (network logon kept, TLS 1.2 or later, a shared cipher suite, the port untouched) |
lawliet_containment_rules_survive | No interface was on a Public network in the last 7 measured days (containment firewall rules would be merged away) |
legacy_tls_clients_absent | No TLS handshake with the protocol and role the change removes was seen in 7 covered days |
ldap_unsigned_binds_absent | No unsigned LDAP bind on any DC (events 2889, 2887) |
ldap_cbt_failures_absent | No LDAP channel binding failure on any DC (event 3039) |
dsrm_logon_not_in_use | Nobody logs on with the DSRM account |
smb_clients_support_encryption | Every SMB client seen speaks SMB 3.0 (3.1.1 for the minimum-dialect control) |
no_lawliet_local_account_credential_for_host | No Lawliet WinRM credential is a local account |
no_lawliet_credential_in_scope | No Lawliet WinRM credential is in the groups the change targets (Domain/Enterprise Admins, Protected Users unless it uses Kerberos) |
lawliet_deploy_uses_wmi_psexec | Lawliet reaches the host only through its agent and WinRM or SSH (no WMI or PsExec) |
domain_joined | The agent reports domain membership |
laps_schema_extended | The AD schema has the Windows LAPS attributes |
domain_join_delegation_in_place | The machine account quota is already 0, or everyone who joined machines through it in 90 days holds create-computer rights where those machines are |
privileged_accounts_have_aes_keys | Every enabled privileged account's password was set after AES keys existed in the domain, and none is DES-only |
unconstrained_delegation_consumers_reviewed | No non-DC account has unconstrained delegation, or the AD finding was acknowledged |
not_a_print_server | The Print Spooler shares no printer (or does not run) |
iis_no_bound_sites | IIS has no started site with a binding and no dependent role (WSUS, AD CS web, RDS web, Web-Mgmt) |
ftp_sites_unused | No inbound FTP (tcp 21 or 990) in a fresh inventory watched 7 days or more |
wins_clients_absent | WINS is absent, or its counters stayed flat 7 measured days, with no partners and no TCP 42 flow |
snmp_not_polled | SNMP is absent or stopped, or has no community. With communities on a running service the answer is unknown (UDP polls are not visible) |
not_a_ras_server | Routing and Remote Access is not running and not configured |
dns_transfer_partners_known | No zone allows transfers, or the transfer counters stayed flat 7 measured days |
stored_credentials_not_in_use | No scheduled task stores a password, and no profile holds saved credentials |
user_store_keys_not_used_by_services | No account that runs a service or task holds a private key in its user store |
bitlocker_recovery_escrow_ok | BitLocker is off (for a non-encrypting control), or a recovery password is backed up to AD |
bitlocker_suspend_for_bcd_change | BitLocker is off, or the fix suspends it for the reboot and the key is backed up |
no_thunderbolt_network_or_dock_in_use | Nothing was used behind Thunderbolt or FireWire in 7 measured days |
kernel_dma_protection_state | The Kernel DMA Protection state is known, and blocking cannot only break a dock it already guards |
vbs_capable_hardware | Virtualization-based security runs, or the hardware supports it with Secure Boot |
lawliet_agent_defender_exclusion | The agent relies on no local-only Microsoft Defender exclusion |
Ubuntu guards#
Missing data is unknown (blocks). None is overridable except the baseline guard. The control's own script repeats its checks on the host before it changes anything.
| Guard | Passes when |
|---|---|
service_not_in_use | The services and ports the change stops have no inbound baseline client and no use in telemetry |
no_module_in_use | The kernel modules it blacklists are not loaded, back no mount and are needed by no baseline process |
lawliet_ssh_credential_keeps_working | No Lawliet SSH credential is refused or loses its port |
lawliet_ssh_algorithms_compatible | The SSH algorithms the change keeps include one Lawliet's SSH client can use |
lawliet_agent_and_server_ports_allowed | Firewall rules keep the agent's server ports and Lawliet's SSH port |
lawliet_credential_not_lockable | No Lawliet SSH credential signs in with a password (SSH or sudo) |
no_admin_password_expiry | No Lawliet SSH credential depends on an account password |
break_glass_account_exists | A recovery SSH key or break-glass administrator is issued, or Lawliet has a key-based non-root account |
lawliet_sudo_credential_keeps_working | No Lawliet credential relies on passwordless sudo |
audit_storage_headroom | The agent's disk reading (under 60 minutes old) is at most 80% |
audit_rules_final | Every other control of the auditd family passes |
lawliet_agent_audit_watches_loaded | The agent reported its own audit watches loaded in the last 48 hours |
not_a_router_or_container_host | No router, VPN, container or VM software in the baseline; not a firewall asset |
no_rootless_containers | No rootless container runtime or sandboxed browser in the baseline |
not_a_log_collector | No inbound baseline connection to the syslog ports the change closes |
no_asymmetric_routing | The agent reports one routed interface |
ipv6_not_learned_from_ra | No global IPv6 address, or router advertisements are not accepted where there is one |
no_active_ssh_tunnels | sshd has no extra listener and no outbound connection in the baseline |
Behind NAT or a port forward. These guards judge a credential by the port it reached the host on. When Lawliet reaches SSH or WinRM through a forwarded port (for example 2262 on a gateway forwarded to the host's 22), a firewall change that keeps 22 open is refused as cutting Lawliet off. Declare the path as the host sees it in Hardening > Planner > Management paths (41.9). The guards then use the declared port and name the declaration in their explanation.
41.5 Procedure: stage the audit step#
Some controls are audit_then_enforce: denying NTLM on a host or in the domain, allowing only AES in Kerberos, blocking the coercion RPC interfaces, every Attack Surface Reduction rule, LDAP signing and channel binding, and others. They are enforced only after an audit step has shown who would be affected.
Permission: Run hardening. Four-eyes: no.
Risk: staging changes nothing the change itself would change. On the host it may switch on more auditing for the signals the control names (for example NTLM auditing, Kerberos service-ticket auditing, LDAP interface diagnostics), which writes more events to the host's logs. The agent refuses a setting that could lose events or halt the host (45.4). Rollback: cancel the audit step (below); the agent switches back exactly what it switched on.
- Build the plan for the host (41.1).
- Tick the controls with the audit first chip.
- Press Stage audit (N).
What you see: "Audit staged for N control(s)." and, per control, "<id>: window started" (or "already running"), followed by "turn on: <setting>" for each audit setting the agent will switch on. For a change written to a domain Group Policy the step is opened on every domain controller of the domain that has an agent; the server names the DCs without one (their usage stays unknown).
What happens next:
- The agent collects the control's signals (and the on-demand ones, such as every Kerberos service ticket, only while the step is open).
- The step is complete when every signal has 7 days of measured coverage on the host (on every DC for a domain-wide change). A control with no signal, or with a signal no agent can observe, completes when its staged window has run, and its enforce then needs the acknowledgement (41.6).
- An open step lasts at most 30 days; staging again starts a new one.
- The card shows audit complete, and Request enforce includes the control.
For NTLM restriction the server also requires NTLM auditing seen on the host for at least 7 days (HARDENING_NTLM_AUDIT_MIN_DAYS) in a log that still reaches back that far, from data no older than 14 days, with nothing unreviewed; RC4 removal is checked on every DC of the domain. Every NTLM use the audit saw that is not yet an exception is listed with its account and program for you to tick in the Fix plan; nothing is filled in for you. The host checks its own last 7 days of events again before it changes anything.
If it fails:
| Message | Cause | Fix |
|---|---|---|
| 422 "These controls are enforced directly; they have no audit stage." | The selection holds an enforce control | Select only audit first controls |
| 422 "Accepting the log volume needs a second administrator ..." | The request asked to accept the log volume itself | Stage without it, then use the four-eyes call below |
Accept the log volume on a small log (API, four-eyes)#
On a domain controller, some settings log every Kerberos ticket, TLS handshake, LDAP bind or domain NTLM authentication. The agent refuses to switch such a high-volume setting on while the log it writes to is smaller than 1 GiB, so the events of the audit step do not push everything else out of the log. Enlarge the log (Group Policy or wevtutil sl <log> /ms:<bytes>) and the refusal clears at the agent's next daily check - the recommended path.
To let the agent switch them on into the smaller log instead:
Permission: Run hardening. Four-eyes: yes, always: the call is refused (409 FOUR_EYES_REQUIRED) unless dual control is on for "Execute remediation" with at least two approvers who can sign for each other.
- The stage-audit answer lists, under
accept_log_volume_request, the hosts, the settings and the log each writes. - Send
POST /api/v1/hardening/planner/hosts/{agent_id}/accept-log-volumewithcontrol_ids,hostsexactly as listed, and ajustificationof at least 10 characters. With dual control on, the answer is 428: the request is held for a second signature. - A second administrator approves; the approver reads "High-volume audit logging on <host> [<setting>><log>,...]; <host> [...] for <controls>". Execute it from Approvals > My requests. The answer lists what was accepted per host; each agent gets the acceptance with its next policy and switches those settings on.
Risk: the log rolls over sooner, so older events in it are lost earlier. Rollback: cancel the audit step; the agent switches the setting back. If the hosts or settings changed before the approved request runs it is refused (409 ACCEPT_CHANGED, with the current list to sign).
Cancel an audit step (API)#
There is no console button for this in this release.
Permission: Run hardening. Four-eyes: no. Send POST /api/v1/hardening/planner/hosts/{agent_id}/cancel-audit with control_ids. For a domain-wide control the steps on every DC are cancelled too. Each agent then switches back the audit settings it switched on for those steps (keeping any that another open step still needs). The step also closes by itself when the control's enforce completes, after 30 days, when the agent is uninstalled, or when the agent is deleted from the server. "These controls have no open audit step on this host." (404) means there is nothing to cancel.
41.6 Procedure: request an enforce#
Permission: Run hardening. Four-eyes: the request is one signature; it creates a hardening run held in Pending approval, and approving the run is the critical action "Execute remediation" (26.2).
Risk: once approved and executed, the change is made on the host. Each step records the value it replaces. Rollback: Rollback on the run (26.3) puts the recorded values back; a step whose rollback could not restore the value says so.
Before you start: if a selected control can lock you out of the host, issue its recovery artifact (26.5).
- Build the plan for the host (41.1). Read each selected card's guards and predicted breakage.
- Tick the controls to enforce. Request enforce (N) counts every selected control that is not manual.
- If a selected control's breakage is predicted or unknown, the bar shows Accept breakage risk (N) and a Justification (recorded in the audit log) field. Tick the box and type why (at least 10 characters) - for example the clients named have been moved off.
- Press Request enforce (N).
- If a control can lock you out, the lockout box opens (41.8).
What you see: the server's message, a line per created run "Execution N (pending_approval): <controls>", and Open Hardening to approve (second administrator). Then approve on Hardening (26.2).
The request is checked in this order; the first that fails refuses it, and nothing is written:
| Refusal | Meaning | What to do |
|---|---|---|
409 GUARD_BLOCKED "A guard blocks enforcing these controls; fix the cause first." + "Fix in this order:" | A guard failed or is unknown | Follow the numbered fix order; for an overridable unknown or baseline finding see 41.7 |
409 GUARD_CHANGED | The baseline finding changed since the override was requested | Plan again and request a new override |
409 ORDER_BLOCKED "These controls cannot be enforced yet." | A prerequisite does not pass yet ("enforce and validate first: <ids>"), a conflict, a cycle, or a manual control | Enforce the prerequisites first; drop manual controls |
409 ALREADY_REQUESTED | An enforce of the control is already waiting or running | Approve or cancel that run first |
409 AUDIT_STAGE_REQUIRED "Stage the audit first and let it run until telemetry covers 7 days." | The audit step is missing or not complete | 41.5 |
409 BREAKAGE_PREDICTED "Enforcing these breaks clients seen using them." / BREAKAGE_UNCERTAIN | Predicted or unknown breakage not acknowledged | Move the named clients off, or tick Accept breakage risk with a justification |
422 JUSTIFICATION_REQUIRED | Accepting breakage without at least 10 characters of justification | Type the justification |
409 LOCKOUT_RISK | A control can lock you out | The lockout box (41.8) |
422 NOT_IN_PLAN | A control is not in the host's current plan | Plan again |
The checks run again when the run is approved, against the host as it is then. A client that started using the removed feature after the request (for example new NTLM use while the run waited) is "new use seen since the enforce was requested" and blocks the approval. A run still waiting after HARDENING_PENDING_EXPIRY_HOURS (default 24) expires: "This execution waited for approval longer than allowed and has expired; request it again so the guards are checked against the host as it is now."
The same checks on every route#
The Fix plan on a scan (22.4) and the older hardening calls (POST /api/v1/hardening/executions and the compliance harden call) run the same guards, order, audit gate and baseline check for every catalog control, and refuse (409) instead of skipping. Outside the planner nothing can be acknowledged, so an enforce whose breakage needs an acknowledgement must be requested through the planner. The Fix plan shows such a control as "Not yet: <reason>".
41.7 Procedure: override with approval#
Use it only when the cause cannot be fixed first and a second administrator agrees. It covers two kinds of block, and only these:
- the do-not-disrupt baseline guard (
protect_host_baseline): a protected item would be disrupted, the host has no approved baseline yet, or the effect cannot be judged; - a Windows guard whose answer is unknown because its data is missing or stale.
A measured fail is never overridable, and neither is any Ubuntu guard or Lawliet-access guard.
Permission: Run hardening. Four-eyes: yes, always. The console offers the button only for controls whose every blocking guard is overridable; the server refuses the request (409 "Overriding the host baseline needs a second administrator: turn on dual control for 'Execute remediation' with at least two approvers, or fix the cause instead.") unless dual control is on for "Execute remediation" with two approvers who can sign for each other.
Risk: the change may disrupt what the baseline protects (a service, a port, a package, a protocol in use), or a guard's risk that could not be measured. Rollback: Rollback on the run (26.3).
- Build the plan, tick the guarded controls.
- Type the Justification (recorded in the audit log) - at least 10 characters.
- Tick Accept breakage risk if shown.
- Press Override baseline with approval (N) ("Enforce although the do-not-disrupt baseline guard blocks it; a second administrator must sign").
- A second administrator approves in Approvals; then press Execute now in Approvals > My requests. This creates the hardening run, which is approved on Hardening like any other.
The approval covers exactly the finding you saw: the request carries a digest of each overridden guard. If the finding changed before the request runs, it is refused (409 GUARD_CHANGED: "The baseline finding changed since the override was requested; plan again and request a new override."). Every other guard still applies. The override is recorded on the run and in the audit trail.
After an upgrade to 1.5.14.s: an override signed under an earlier version does not carry this digest and is refused when its run is approved. Plan the host again and request the override again through the planner. For overrides made directly on the baseline, see 42.6.
41.8 Lockout-risk controls#
A control that can lock you out of the host (key-only SSH, firewall lockdown, a WinRM change, disabling the built-in administrator, disk encryption) is refused on the first request with LOCKOUT_RISK. The planner then shows the red box "This control can lock you out of the host" (or "N controls can lock you out of the host"), with each control's id and name, Risk: and Before you start: (the recovery artifact to issue).
- Issue the recovery artifact (26.5).
- Type ACCEPT LOCKOUT RISK in the box ("Issue the recovery artifact first. To start anyway, type ACCEPT LOCKOUT RISK. The run still waits for approval.").
- Press Start with lockout risk accepted. Cancel sends nothing.
The request sent is exactly the one that was refused. The run then needs an approver other than the requester; the run's detail on Hardening says "Someone other than the requester must approve this run: it carries an accepted lockout risk or a risky re-apply. Make sure the host's recovery artifact has been issued."
41.9 Procedure: declare a management path (NAT or port forward)#
Open it from Hardening > Planner > Management paths (top right). Use it when Lawliet reaches a host's SSH or WinRM through NAT or a port forward, so the port a deployment credential reached the host on (for example 2262 on a gateway) is not the port the host listens on (22). Without a declaration, the firewall and credential guards judge the outside port. A firewall change that keeps 22 open is then refused, and the guard's fix list points here.
The page lists every declaration. Each entry shows the host or host group, each path ("SSH arrives at port 22"), Covers: (the hosts it applies to), the justification and who declared it. A host listed in the group after the declaration was signed appears under "Listed since it was signed, not covered until a new declaration is signed". Declare again to cover it.
Declare a path
- For: one host or a host group (its listed hosts). A group declaration covers only the hosts listed in the group by an administrator, never hosts that are members through an asset tag. It also covers only the hosts listed when the declaration is signed.
- Host / Host group: choose it. Covers shows the hosts the request will name.
- SSH port the host sees and/or WinRM port the host sees. For one host you may also give The host's address the connection arrives at. A group declaration has no address. Management source networks: the addresses or networks the hosts are administered from, as the hosts see them (comma or space separated, at most 32). Refused: anything wider than /8 (IPv4) or /32 (IPv6), 0.0.0.0/0 and ::/0; loopback, unspecified, link-local and multicast space; IPv6 prefixes that carry IPv4 space wider than /8 (::ffff:0:0/96, 64:ff9b::/96, 6to4 2002::/16) and ::/96; an IPv6 zone id (
%...) or any character that is not part of an address. The firewall scripts apply the same rules. A declaration may name ports, networks or both. - Why: at least 10 characters. The second administrator reads it.
- Press Request declaration. The request goes to a second administrator (dual control on Execute remediation). The approval names the host or group, every host it covers, and each address:port.
The request is refused before any approver is asked when:
- the host's own inventory shows nothing serving that port: sshd (or systemd for socket-activated ssh.socket) for SSH, System or svchost for WinRM (
NOT_LISTENING); - the request no longer names the host or the group's current listed hosts (
TARGET_CHANGED, also checked when the approved request runs).
Where the inventory cannot tell (ports never reported, or the report cut at the port cap), the declaration is applied and the guard's text says it is "not corroborated". A declaration stops applying to a host whose complete inventory no longer shows the server on that port.
When the planner uses a declaration, the guard's explanation names it: "arrives at 10.0.2.15:22 on the host (reached as port 2262), declared for this host by ...". Where both a host's own declaration and a group declaration exist, the host's own wins. Groups that disagree declare nothing.
What the source networks change. A hardening run on a covered host passes them to the firewall controls as their declared sources: the Ubuntu firewall controls and the legacy Linux firewall remediations (NIST CSF PR.AC-5, ISO 27001 A.8.20, PCI DSS 1.4.1 / 1.3.1, SOC 2 CC6.1, CIS 3.4.2 / 3.4.3) keep SSH open for exactly those networks besides the sources they find themselves. On Windows, WIN-AUTH-WINRM-SOURCES (WinRM from management hosts) uses them as its client ranges when the request names none; a request's own ranges win. WIN-SRV-FW-MGMT-OPEN-TO-ANY is a report with a runbook and takes no networks. The entry shows them as "Managed from ...", and the approval names them. The host's own declaration wins over a group's; groups that list a host and name different networks declare none for it: the entry then shows "<host>: no source networks are used" with the groups and their networks, and WIN-AUTH-WINRM-SOURCES says so in its refusal. A host's own declaration that names only ports does not hide its groups' networks. A request that names no WinRM ranges is checked with the declared networks: the guards, the check that your own address is in the list, and the script all use the same ranges.
Remove a path: press Remove on the entry. This also needs a second administrator: in a reverse topology (outside 22, host 2222), removing the declaration would make the guards judge 22 again and let a change close 2222. Deleting a host or a host group removes its declaration.

