Auditors do not accept “we think it is secure.” ISO 27001 Annex A, NIS2 Article 21, and DORA’s ICT risk requirements all demand documented, repeatable security controls with evidence of implementation. A Linux hardening baseline is the foundation that makes every other control credible. This guide covers the configuration layer: what to set, why it matters, and how to verify it on a cloud VPS or bare-metal server running a regulated European workload.
The steps below follow the logic of the CIS benchmark for Linux, adapted for cloud deployments where some physical controls are irrelevant but network exposure is higher. Work through them in order; later sections assume earlier ones are in place.
Start with the attack surface: users, packages, and services
Every package installed and every service running is a potential entry point. Begin with a minimal OS image, then audit what is actually present. On Debian or Ubuntu, dpkg --get-selections | grep -v deinstall lists installed packages; on RHEL-family systems, use rpm -qa. Remove anything not required by the workload.
Disable and mask services that should never run in production:
- avahi-daemon (mDNS discovery, unnecessary on server infrastructure)
- cups (printing)
- rpcbind unless NFS is explicitly required
- bluetooth on any cloud instance
Use systemctl disable --now <service> and follow with systemctl mask <service> to prevent accidental re-enabling. Verify with systemctl list-units --type=service --state=running.
Local user accounts deserve the same scrutiny. Lock accounts that have no login requirement: usermod -L -s /sbin/nologin <username>. Set a password expiry policy in /etc/login.defs and enforce it with chage. Accounts with UID 0 other than root are a hard finding in any audit; confirm there are none with awk -F: '($3 == 0) {print}' /etc/passwd.
SSH hardening best practices for cloud servers
SSH is the primary administrative interface on any Linux server, which makes it the most scrutinised component in a cloud server security baseline. The default configuration ships with settings that prioritise compatibility over security; production systems need that reversed.
SSH misconfiguration is the single most common finding in infrastructure penetration tests of cloud-hosted Linux systems. Fixing it takes ten minutes and eliminates an entire attack category.
Apply the following in /etc/ssh/sshd_config:
- PermitRootLogin no: root should never authenticate directly over SSH. Use a named admin account with sudo.
- PasswordAuthentication no: key-based authentication only. Passwords are brute-forceable; keys are not.
- PubkeyAuthentication yes
- AuthorizedKeysFile .ssh/authorized_keys: restrict to a single, predictable location.
- MaxAuthTries 3: limits brute-force attempts per connection.
- LoginGraceTime 30: close unauthenticated connections quickly.
- AllowUsers or AllowGroups: explicitly whitelist which accounts may connect.
- Protocol 2: SSHv1 is broken; this should already be the default but verify it explicitly.
- Ciphers: restrict to
chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com. - MACs: restrict to
hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com. - KexAlgorithms: remove diffie-hellman-group1-sha1 and group14-sha1.
- X11Forwarding no and AllowTcpForwarding no unless explicitly needed.
- Banner /etc/issue.net: a legal warning banner satisfies audit requirements and is required under several sector frameworks.
Reload with sshd -t to validate the configuration before applying it, then systemctl reload sshd. Test from a second session before closing the existing one.
Linux firewall configuration: default deny at the host level
Cloud provider security groups are a perimeter control. Host-level firewall configuration is a defence-in-depth control. Both are required under CIS benchmark Linux Level 1; relying solely on the provider layer fails the principle of independent controls that NIS2 and ISO 27001 expect.
nftables is the current standard on modern Linux distributions, replacing iptables as the kernel’s primary packet filtering interface. A minimal baseline ruleset looks like this:
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
tcp dport 22 ct state new accept
# add application ports here
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Default-drop on input, explicit accept for established connections, loopback, and SSH. Everything else is denied until explicitly permitted. Enable and persist with systemctl enable --now nftables and save the ruleset to /etc/nftables.conf.
Kernel hardening via sysctl
The kernel’s runtime parameters control network stack behaviour and memory protections. These settings belong in /etc/sysctl.d/99-hardening.conf and are applied with sysctl --system:
- net.ipv4.ip_forward = 0: disable IP forwarding unless this is a router.
- net.ipv4.conf.all.accept_redirects = 0 and net.ipv6.conf.all.accept_redirects = 0: reject ICMP redirects.
- net.ipv4.conf.all.rp_filter = 1: enable reverse path filtering to block spoofed packets.
- net.ipv4.tcp_syncookies = 1: SYN flood protection.
- kernel.randomize_va_space = 2: full ASLR.
- kernel.dmesg_restrict = 1: prevent unprivileged users reading kernel ring buffer.
- fs.protected_hardlinks = 1 and fs.protected_symlinks = 1: block common privilege escalation vectors.
- kernel.yama.ptrace_scope = 1: restrict ptrace to parent processes.
Audit logging and file integrity monitoring
Controls are only auditable if there is a log trail. The Linux Audit Framework (auditd) provides structured, tamper-evident logging of system calls. Install it, enable it, and configure rules in /etc/audit/rules.d/ to log authentication events, privilege escalation, changes to /etc/passwd, /etc/shadow, and /etc/sudoers, and any execution of commands via sudo.
For file integrity monitoring, AIDE (Advanced Intrusion Detection Environment) generates a cryptographic baseline of the filesystem and alerts on unexpected changes. Initialise with aide --init, move the database to a read-only or remote location, and schedule checks via cron or a systemd timer. Under DORA’s ICT integrity requirements and NIS2’s incident detection obligations, this is not optional for regulated workloads; it is the evidence that change detection controls exist.
Forward logs off-host immediately. A compromised server can manipulate local logs; a remote syslog or SIEM cannot be altered retroactively by the same attacker. On European infrastructure, that log destination should itself sit within EU jurisdiction to maintain GDPR compliance for any personal data captured in logs.
Keeping the baseline current
A hardening baseline documented once and never revisited degrades. Kernel vulnerabilities, new CVEs in OpenSSH, and updated CIS benchmark Linux recommendations all require periodic review. Build the baseline into your change management process: any OS upgrade, major package update, or architecture change triggers a re-validation against the documented controls.
Automate verification. Tools like oscap from the OpenSCAP project can scan a running system against a SCAP profile and produce a machine-readable compliance report, which is exactly the evidence format that ISO 27001 auditors and NIS2 competent authorities accept. Schedule monthly scans and retain the reports as part of your information security management system documentation.
The configuration above is a baseline, not a ceiling. Workloads handling payment data, health records, or critical infrastructure will need additional controls layered on top. But every regulated Linux deployment in Europe should be able to demonstrate, with evidence, that this foundation is in place.




