Hardening Field Report
Author
A systematic hardening of three hosts - home server and two cloud VPS - toward a minimal attack surface and no public admin plane. The trigger was making the background radiation of the internet visible: a constant stream of SSH brute-force attempts that the SIEM surfaced the moment agents went live.
- UFW
- fail2ban
- Tailscale
- SSH
- Wazuh
THE SIGNAL
The moment Wazuh agents started shipping SSH logs, the volume became impossible to ignore: tens of thousands of failed authentication attempts hammering every host with a public IP. It's background noise on the internet, but it's a reminder that anything reachable is probed constantly. The hardening pass turned that signal into a closed door.
SSH OFF THE PUBLIC INTERNET
Every host now accepts SSH only from the Tailscale network; public port 22 is closed to the world. This is the single largest reduction in attack surface: brute-force bots cannot reach sshd at all, because the firewall drops the packet before the daemon sees it. Authentication is key-only across all hosts - password auth is disabled. One cloud host had been left on password auth during initial setup; the hardening audit caught and corrected it.
FIREWALL AND THE DOCKER GOTCHA
UFW runs on every host with an explicit allowlist: HTTPS is the only public exposure; everything administrative is scoped to the Tailscale CIDR. App ports that don't need public exposure are bound to localhost, so the reverse proxy is the sole ingress - which also neutralises the classic Docker-bypasses-UFW problem, where Docker's own iptables rules can make a container port reachable even when UFW says it's blocked. Understanding and containing that interaction was a meaningful part of the hardening work.
fail2ban sits behind UFW as a second layer with a short retry window and a 24-hour ban, providing defence-in-depth for any service that does reach the public internet.
PATCHING
Wazuh's vulnerability detector gives a live CVE inventory per agent. A coordinated patching pass - apt upgrades plus stale-kernel cleanup - pulled the reported CVE count down by roughly half on the production host. The remainder are in packages with no upstream patch yet; they're tracked, not ignored.
WHAT IT BOUGHT
Brute-force bots now hit a closed port. No public admin surface remains. The firewall/Docker interaction is understood and contained rather than assumed away. And the noise that's left is visible in the SIEM and accounted for.
KEY TAKEAWAYS
- SSH closed to the public internet on all hosts - Tailscale-only access
- Brute-force bots never reach the SSH daemon: firewall drops before the daemon sees the packet
- Docker-bypasses-UFW interaction understood and neutralised with localhost port binds
- Defense in depth: UFW allowlist + fail2ban + localhost binds, layered
- CVE inventory via Wazuh; patching halved the reported count on the production host