Basic Linux Hardening Steps That Dont Break Workflows
Introduction
Security hardening should raise the bar for attackers without frustrating your team. Start with reversible, low-risk changes that reduce exposure and add visibility, then iterate.
Principles that wont break things
- Incremental & reversible: change one thing at a time; keep rollback commands handy.
- Stage & test: apply on a non-prod host first; monitor logs/metrics for 2448h.
- Prefer allowlists over denylists: smaller blast radius, clearer intent.
- Log everything you can: visibility beats guesswork when something misbehaves.
Quick wins (safe defaults)
| Step | Why | Sample command |
|---|---|---|
| Disable unused services | Shrinks attack surface & resource use | systemctl --type=service --state=runningsudo systemctl disable --now avahi-daemon |
| Keep SSH but harden it | Stops easy brute force; keeps remote access | sudoedit /etc/ssh/sshd_config (see below), then sudo systemctl reload sshd |
| Apply regular updates | Patches known vulns with minimal change | Debian/Ubuntu: sudo apt update && sudo apt upgrade -yRHEL/Fedora: sudo dnf upgrade -y |
| Firewall: default deny inbound | Only expose what you intend | UFW: sudo ufw default deny incoming; sudo ufw allow OpenSSH; sudo ufw enable |
| Tighten sudo | Least privilege; better audit | sudo visudo -f /etc/sudoers.d/10-team (see example) |
| Add fail2ban | Blocks repeated auth failures | sudo apt install fail2ban / sudo dnf install fail2ban |
| Persist system logs | Keep evidence across reboots | sudo sed -i 's/^#\?Storage=.*/Storage=persistent/' /etc/systemd/journald.conf && sudo systemctl restart systemd-journald |
SSH hardening (safe, incremental)
Keep SSH accessible, but reduce brute force and risky defaults. Test from a second session before reloading.
# /etc/ssh/sshd_config (add or ensure these)
Protocol 2
PermitRootLogin no
PasswordAuthentication yes # keep for now; switch to 'no' after keys are deployed
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
AllowGroups sshusers # create & add users you trust
# Optional: migrate by subnet (dont lock yourself out)
# Match Address 10.0.0.0/8
# PasswordAuthentication no
Then:
sudo groupadd -f sshusers
sudo usermod -aG sshusers <youruser>
sudo sshd -t && sudo systemctl reload sshd
Once keys are deployed for all admins, set PasswordAuthentication no and consider MFA (PAM) later.
Firewall examples (UFW & nftables)
UFW (easy)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
# add services you actually serve
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo ufw status verbose
nftables (minimal)
sudo tee /etc/nftables.conf >/dev/null <<'EOF'
table inet filter {
chain input {
type filter hook input priority 0;
ct state established,related accept
iif lo accept
tcp dport {22,80,443} accept
icmp type echo-request limit rate 5/second accept
reject with icmpx type admin-prohibited
}
chain forward { type filter hook forward priority 0; drop; }
chain output { type filter hook output priority 0; accept; }
}
EOF
sudo systemctl enable --now nftables
Updates without surprises
- Schedule a weekly window for kernel & service restarts.
- Enable unattended security updates (Debian/Ubuntu):
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
- RHEL/Fedora: use
dnf-automaticfor security advisories only.
Sudo: least privilege with audit
# /etc/sudoers.d/10-team (edit with visudo)
Defaults timestamp_timeout=10
%wheel ALL=(ALL) ALL
# Example: specific admins can restart a service without full root:
%deploy ALL=(root) NOPASSWD: /bin/systemctl restart myapp.service
Add users to groups instead of granting user-by-user sudo rules:
sudo usermod -aG wheel <admin>
sudo groupadd -f deploy && sudo usermod -aG deploy <operator>
Fail2ban (default jail for SSH)
# /etc/fail2ban/jail.local
[sshd]
enabled = true
maxretry = 5
bantime = 1h
findtime = 10m
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Logging & housekeeping
- Persist journals: set
Storage=persistent(see quick wins). - Logrotate: ensure defaults are present; avoid filling disks.
- Check world-writable spots (notification only):
sudo find / -xdev -type d -perm -0002 ! -path "/proc/*" 2>/dev/null
Low-risk sysctl tweaks
# /etc/sysctl.d/60-safe-hardening.conf
net.ipv4.tcp_syncookies = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
# apply
sudo sysctl --system
Avoid aggressive kernel changes on production first; roll out after staging validation.
Impact
These steps balance security and usability: fewer exposed services, safer remote access, predictable updates, and better visibilitywithout breaking day-to-day workflows.
Quick checklist
| Item | Status |
|---|---|
| Unused services disabled | ? |
| SSH hardened & tested | ? |
| Firewall default deny inbound | ? |
| Security updates scheduled | ? |
| Sudo least privilege in place | ? |
| Fail2ban active | ? |
| Logs persistent & rotated | ? |
| Safe sysctl applied | ? |
Conclusion
Start with these low-risk hardening steps, monitor the effect, and iterate. The goal is steady security gains with zero disruption to your teams productivity.