Hardening engine
The HostSSH one-click + granular server-hardening engine — controls, profiles, the check/apply/revert/drift model, and the lockout-protection invariants that let it run as root without bricking a box.
Hardening engine
One-click hardening is a HostSSH headline differentiator: a customer drops the agent on a raw VPS, clicks
Secure my server, and the box is hardened to a sane baseline — with a visible Security Score, a
granular override grid, and a hard guarantee that it cannot lock them out. This page is the engineering
contract for that engine. It is net-new (internal/harden), but it rides the rails the agent already has
(privileged shell-outs with arg-vectors, the on-disk atomic state.Store, the telemetry heartbeat).
Why this matters competitively: Coolify has no hardening, Cockpit/CIS-CAT/Ansible roles have no armed auto-revert. The lockout-safety below is the reason a non-expert will actually press the button.
Package shape
agent/internal/harden/
harden.go Engine: Check / Apply / Revert / Drift over a profile
controls/ one file per control (sshd, firewall, updates, sysctl, fail2ban-equiv, users)
profile.go baseline | strict | cis-l1 (+ --only / --except selectors)
snapshot.go capture current state of each control before any change
revert.go armed auto-revert timer + manual revert
score.go posture score (0–100) computation → heartbeat sample
CLI surface (parity with dashboard + MCP):
hostssh harden --profile baseline|strict|cis-l1 --check # report only, no changes
hostssh harden --profile baseline --apply [--only sshd,firewall]
hostssh harden --revert [--only sshd] # roll back to the pre-apply snapshot
hostssh harden --drift # what has drifted from the applied profile
Each verb emits the same progress stream the other RPCs use, so
the dashboard, CLI --json, and MCP all watch identical events.
Controls (v1)
Every control is idempotent, check-first, snapshotted, and revertible with a visible diff.
| Control | What it sets (baseline) | Notes |
|---|---|---|
sshd | disable root password login, disable password auth (only if a working key/cert is proven present), Protocol 2, sane ciphers/MACs, MaxAuthTries, LoginGraceTime | never removes a working credential; validated with sshd -t before reload |
firewall | default-deny inbound, allow the agent's own egress/tunnel first, allow only granted ports | nft -c check before commit; must never sever the channel that would revert it |
updates | enable unattended-upgrades (security pocket) | distro-aware (apt now; dnf later) |
sysctl | kernel network/memory hardening (rp_filter, syncookies, kptr_restrict, dmesg_restrict, disable redirects) | applied via drop-in, not in-place edits |
intrusion | CrowdSec Security Engine (MIT) — brute-force/scan blocking | chosen over fail2ban (GPL — banned by our license rule) |
users | audit sudoers, lock unused system accounts, enforce auth log retention | report-heavy in v1 |
mfa (GA) | OS-level MFA for SSH where applicable | gated behind the access work |
License note: the native engine is our own code shelling to permissive tools only. Banned for parity: fail2ban (GPL), Cockpit (LGPL/GPL), CIS-CAT (proprietary), Lynis is GPL — so an optional external Lynis/OpenSCAP score is opt-in shell-out, never bundled; the shipped scorer is ours.
The non-negotiable: it cannot lock you out
The engine runs as root and touches sshd + the firewall, so a naive apply can strand the box. These
invariants are agent-side and undisableable for the sshd and firewall controls:
- Validate before commit.
sshd -t,nft -c(or equivalent) must pass before anything is reloaded. - Snapshot before change. The prior state of every touched control is written to
state.Storefirst. - Armed auto-revert. A network/auth change starts a timer: if a fresh successful session or heartbeat does not confirm reachability within N minutes (default 10), the change auto-rolls-back. This is the safety net that makes one-click hardening safe for novices.
- Egress-first firewall ordering. The agent's own outbound channel/tunnel is allow-listed before the default-deny rule lands — the path that would perform the revert is never the path that gets cut.
- Credential proof gate. Password auth is only disabled if a working key/cert is provably present.
- Staged fleet fan-out. Multi-box hardening is canary → staged, halt-on-first-failure, never all-at-once.
Every apply is therefore: check → snapshot → validate → commit → arm auto-revert → confirm → disarm.
Posture score + drift
score.go reduces the live control states to a 0–100 posture score, pushed as a sample on the
existing telemetry heartbeat. The dashboard renders it as the animated
Security Score, the per-control pass/fail grid, and a drift alert when a previously-applied control no
longer matches the profile (someone hand-edited sshd_config, a package update reset a sysctl, etc.).
Drift is surfaced, not silently re-applied — re-hardening is an explicit action.
Status
Net-new for the control-panel phase. Today the only hardening-adjacent code is the draft
infra/bootstrap.sh, which has none of the safety above (no validate, no snapshot, no auto-revert) —
the engine generalizes its intent with the invariants made mandatory. Ships before browser-root-SSH in
the build order.
See also: PLATFORM · agent-protocol · Security · user: Hardening · user: Web-SSH