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.

ControlWhat it sets (baseline)Notes
sshddisable root password login, disable password auth (only if a working key/cert is proven present), Protocol 2, sane ciphers/MACs, MaxAuthTries, LoginGraceTimenever removes a working credential; validated with sshd -t before reload
firewalldefault-deny inbound, allow the agent's own egress/tunnel first, allow only granted portsnft -c check before commit; must never sever the channel that would revert it
updatesenable unattended-upgrades (security pocket)distro-aware (apt now; dnf later)
sysctlkernel network/memory hardening (rp_filter, syncookies, kptr_restrict, dmesg_restrict, disable redirects)applied via drop-in, not in-place edits
intrusionCrowdSec Security Engine (MIT) — brute-force/scan blockingchosen over fail2ban (GPL — banned by our license rule)
usersaudit sudoers, lock unused system accounts, enforce auth log retentionreport-heavy in v1
mfa (GA)OS-level MFA for SSH where applicablegated 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:

  1. Validate before commit. sshd -t, nft -c (or equivalent) must pass before anything is reloaded.
  2. Snapshot before change. The prior state of every touched control is written to state.Store first.
  3. 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.
  4. 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.
  5. Credential proof gate. Password auth is only disabled if a working key/cert is provably present.
  6. 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