DEV Community

Cover image for Beyond `apt upgrade`: Automating Linux Hardening for Public Sector Workloads
patrickbloem-it
patrickbloem-it

Posted on • Originally published at dev.to

Beyond `apt upgrade`: Automating Linux Hardening for Public Sector Workloads

The Myth of the "Secure Default"

There is a prevalent misconception in public sector IT that deploying an LTS release of Ubuntu or Debian implies a baseline of security. It does not. It implies stability, not hardening.

A standard cloud image is designed for compatibility and onboarding friction reduction. It is engineered to ensure that ssh root@<ip> works immediately. Conversely, a BSI-compliant or CIS-hardened system is designed for isolation and auditability. These two design philosophies are mutually exclusive.

In regulated environments—specifically under BSI IT-Grundschutz (SYS.1.3) or GDPR Art. 32 requirements—manual hardening is an anti-pattern. If you are editing /etc/ssh/sshd_config by hand in 2025, you have already failed the audit. You cannot prove consistency across 50 nodes if your configuration method relies on human memory.

This article outlines an architectural approach to automated, idempotent server hardening, moving beyond simple package updates to systemic attack surface reduction.

The Compliance Gap

When we deploy a fresh Debian 12 or Ubuntu 24.04 image, we inherit technical debt immediately. Let's look at the delta between a "Fresh Install" and a "Compliance-Ready" state:

Component Default State Required State (CIS/BSI) The Risk
SSH Port 22, Password Auth Port 2222 (obscurity), Key-Only, Crypto Policies Brute-force botnets, Credential Stuffing
Kernel IPv4 Forwarding disabled (mostly), ICMP Redirects enabled accept_redirects=0, dmesg_restrict=1, bpf_jit_harden=2 MITM, Kernel Pointer Leaks, eBPF exploits
Audit auditd package often missing Rules for execve, passwd, sudo No forensic trail for privilege escalation
FS /tmp executable noexec, nosuid, nodev on tmpfs Malware execution in world-writable dirs

Architecture of an Automated Hardening Pipeline

We do not write "scripts". We write state enforcement modules. Whether you use Ansible, Salt, or a bootstrap shell framework, the logic remains identical.

The repository hardened-vps-bootstrap (linked below) implements this logic in pure Bash to remain dependency-free on air-gapped systems.

1. SSH: Crypto Policy and Obscurity

Changing the SSH port is controversial. Purists argue it is "Security by Obscurity". In practice, moving SSH to port 2222 (or higher) reduces log noise by approximately 99%. This is not about hiding from a targeted attacker; it is about reducing the signal-to-noise ratio so your SIEM can actually detect the targeted attacker.

The Implementation:

Force Post-Quantum and High-Security Ciphers
echo "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com" >> /etc/ssh/sshd_config
echo "KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256" >> /etc/ssh/sshd_config
echo "MACs hmac-sha2-512-etm@openssh.com" >> /etc/ssh/sshd_config

Disable Legacy Auth
sed -i 's/^#?PasswordAuthentication./PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#?PermitRootLogin./PermitRootLogin no/' /etc/ssh/sshd_config

text

We explicitly disable PasswordAuthentication. Relying on weak passwords in an era of GPU-accelerated cracking clusters is negligence.

2. Kernel Hardening: The Silent Layer

The kernel network stack is permissive by default. We need to lock down ICMP handling and memory access.

Key Sysctl Parameters:

  • net.ipv4.tcp_syncookies = 1: Essential protection against SYN flood DoS attacks.
  • net.ipv4.conf.all.accept_redirects = 0: Prevents a rogue router on the same subnet from manipulating routing tables.
  • kernel.dmesg_restrict = 1: Prevents unprivileged users from viewing the kernel ring buffer (dmesg), which can leak memory addresses useful for exploit development (ASLR bypass).
  • kernel.unprivileged_bpf_disabled = 1: Disables unprivileged eBPF usage. Recent kernel vulnerabilities often leverage eBPF; if your web app doesn't need it, disable it.

3. Audit Trails: The "Flight Recorder"

Installing auditd is useless without rules. Standard rulesets often miss the critical vector: Execution.

We need to know what commands were run, not just who logged in.

/etc/audit/rules.d/exec.rules
Capture all command executions (sys_execve) for valid UIDs
-a always,exit -F arch=b64 -S execve -F euid>=1000 -F euid!=4294967295 -k audit_cmd

text

This ensures that if an attacker manages to run ./exploit.sh, the execution event—including arguments—is logged to /var/log/audit/audit.log.

Automation vs. Documentation

A runbook is dead the moment it is written. Code is alive.

By encapsulating these hardening steps into a repository, we achieve:

  1. Idempotency: Re-running the script enforces the state again (correcting drift).
  2. Version Control: We can trace when we decided to disable UsePAM via Git commit history.
  3. Speed: Mean Time To Recover (MTTR) drops significantly when server provisioning is automated.

The "Hardened VPS Bootstrap" Repository

I have open-sourced the internal framework I use for public sector infrastructure projects. It is designed to be:

  • Minimal: No Python/Ruby dependencies.
  • Modular: Enable/Disable features via flags.
  • Audit-Ready: Logs every change it makes.

It covers SSH, Sysctl, Fail2Ban/CrowdSec, UFW, and Auto-Updates.

👉 GitHub Repository: patrick-bloem/hardened-vps-bootstrap

Final Thoughts

Security is not a product; it is a configuration state. Standard Linux distributions prioritize the "Out of the Box" experience. As infrastructure engineers, our job is to pivot that priority towards "Secure by Design".

Stop trusting the defaults. Verify your sysctls. Automate your hardening.


About the Author
Patrick Bloem is a Senior Infrastructure Engineer specializing in BSI-compliant Linux environments, ZFS storage solutions, and network segregation in the public sector.

Top comments (0)