# Host Hardening Baseline — SNS Networking **Entity:** SNS Networking · **Status:** Buildable now (Ansible playbook) ## What it is The minimum security posture applied to every Linux host — on-prem VM, LXC, or cloud VPS — before it does anything else. This is the Ansible "base" role that runs first on every new machine. ## Stack | Component | Software | FOSS | Role | |-----------|----------|------|------| | Firewall | UFW (iptables frontend) | Yes | Default-deny inbound, explicit allow | | Brute-force protection | Fail2ban | Yes | SSH + web login jail | | SSH hardening | OpenSSH (config only) | Yes | Key-only, no root, no password | | Auto-updates | unattended-upgrades (Debian) | Yes | Security patches only | | Audit logging | auditd | Yes | File access, privilege escalation events | | Rootkit detection | rkhunter | Yes | Weekly scan, email on findings | ## The Playbook (what it does) ```yaml # roles/base-hardening/tasks/main.yml (conceptual) - name: SSH hardening # PermitRootLogin no # PasswordAuthentication no # PubkeyAuthentication yes # MaxAuthTries 3 # AllowUsers sam - name: UFW default deny # Default incoming: deny # Default outgoing: allow # Allow: SSH (22/tcp) from Tailscale/WireGuard subnet only - name: Fail2ban SSH jail # maxretry: 3 # bantime: 3600 # findtime: 600 - name: Unattended upgrades (security only) # Automatic-Reboot: false (manual kernel reboots) - name: Auditd rules # Watch /etc/passwd, /etc/shadow, /etc/sudoers # Log all sudo usage # Log failed file access attempts - name: Disable unused services # Stop & mask: cups, avahi-daemon, bluetooth (if present) - name: Sysctl hardening # net.ipv4.conf.all.rp_filter = 1 # net.ipv4.icmp_echo_ignore_broadcasts = 1 # net.ipv4.conf.all.accept_redirects = 0 # net.ipv4.conf.all.send_redirects = 0 # kernel.randomize_va_space = 2 ``` ## Per-Host Firewall Rules | Host type | Inbound allowed | Notes | |-----------|----------------|-------| | All hosts | SSH from 10.10.0.0/24 (WireGuard) | Management only over VPN | | Web-facing (Caddy) | 80, 443 from 0.0.0.0/0 | Public web traffic | | Bastion (Linode) | 22, 51820/udp from 0.0.0.0/0 | SSH + WireGuard | | Internal services | App port from service VLAN only | No direct internet access | | Proxmox host | 8006 from Tailscale only | Web UI never on public IP | ## Build Steps 1. Write Ansible role `roles/base-hardening/` with the above tasks. 2. Add to `site.yml` as the first role for all host groups. 3. Run against all existing hosts: `ansible-playbook site.yml --tags hardening` 4. Verify: `ssh root@host` should fail. `ssh sam@host` with key should work. 5. Verify: `nmap -sS host` from outside WireGuard should show only allowed ports. ## Security Posture - **Default deny.** Nothing gets in unless explicitly allowed. - **No passwords anywhere.** SSH key-only. Service accounts use tokens or certs. - **Audit trail.** Every sudo, every sensitive file access logged. - **Automatic patching.** Security updates apply daily without manual intervention. Kernel updates require manual reboot (scheduled maintenance window). ## Upgrade Path - **CIS Benchmark compliance:** Run `cis-cat` scanner and close gaps for client environments that require formal compliance. - **OSSEC/Wazuh:** When you need centralized security event management across 10+ hosts. - **SELinux/AppArmor:** Enforce MAC policies on high-value services (currently not enabled — adds complexity without proportional gain at this scale).