AI · Tech · Science · Crypto · Linux · Gaming · DIY · Guides
🐧 Linux · Linux

Hardening Your Linux Server: A Practical Security Checklist

2769 words · 13 min read

Hardening Your Linux Server: A Practical Security Checklist

Your Linux server is probably more exposed than you think.

That's not an insult—it's arithmetic. The moment a server gets a public IP address, automated scanners find it. Often within minutes. They probe SSH, hunt for outdated packages, and try default credentials while you're still configuring the hostname.

Linux has a well-earned reputation for stability and security, but "Linux is secure" is a dangerous half-truth. The operating system is secure by design. Your configuration of it is a separate question entirely—and that's where most breaches happen.

The numbers back this up. According to the Verizon 2023 Data Breach Investigations Report, over 80% of hacking-related breaches involve brute force or the use of lost or stolen credentials. The Cyentia Institute and Kenna Security found that unpatched vulnerabilities feature in 60% of breaches. And IBM's Cost of a Data Breach Report 2023 puts the average time to identify and contain a breach at 277 days—meaning an attacker who gets in today might not be discovered until next summer.

None of this requires exotic exploits. It requires a server that wasn't hardened.

This checklist covers seven practical steps you can take today, plus a kernel-hardening bonus. Each one is something you can implement on a running server without rebuilding from scratch.

Key Takeaway: Linux security isn't automatic. It's a configuration discipline. The steps below take a few hours to implement and eliminate the vast majority of opportunistic attacks.


1. Lock Down SSH Access

SSH is the front door to your server, and attackers know it. Automated bots hammer port 22 on every public IP, trying common usernames and passwords around the clock. Your first job is making that door much harder to open.

Disable root SSH login. There's no legitimate reason for root to authenticate over SSH directly. Edit /etc/ssh/sshd_config and set:

PermitRootLogin no

This alone eliminates a huge class of brute-force attempts. Attackers know the username root exists—don't give them the target.

Use SSH key pairs instead of passwords. SSH keys are computationally infeasible to brute-force, which is why Fail2ban exists for password-based setups in the first place. Generate a key pair with ssh-keygen -t ed25519, copy the public key to the server, and then disable password authentication entirely:

PasswordAuthentication no
ChallengeResponseAuthentication no

Test your key login in a second terminal before you close the first one. Locking yourself out is a rite of passage, but it's an avoidable one.

Change the default port. Moving SSH to a non-standard port (say, 2222) won't stop a determined attacker, but it dramatically reduces noise from automated scanners. It's not a silver bullet—it's spam filtering for your auth logs.

Limit who can log in. Use AllowUsers or AllowGroups in sshd_config to whitelist exactly who has SSH access. If only three people need to log in, only those three should be able to:

AllowGroups sshusers

Deploy Fail2ban. Even with keys, Fail2ban adds a layer of defense by watching logs and banning IPs that show malicious behavior—repeated failed logins, port scans, and so on. It's a few lines of config for a lot of peace of mind.

Key Takeaway: SSH hardening is the single highest-impact change you can make. Disable root login, switch to keys, and add Fail2ban. Most automated attacks stop here.


2. Configure a Host-Based Firewall

The principle of least privilege applies to network ports just as much as it does to user accounts. If a service doesn't need to be reachable from the internet, it shouldn't be.

Use UFW or firewalld. On Debian and Ubuntu, UFW is the simplest path:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

That's a working firewall in five commands. On RHEL-family systems, firewall-cmd does the same job with zones instead of rules.

Allow only what you need. Ports 22, 80, and 443 cover most web servers. If you're running a database, it should not be exposed to the internet—bind it to localhost or a private interface. Every open port is an invitation.

Don't skip firewalls on internal servers. It's tempting to think "it's behind the VPN, it's fine." But lateral movement is how attackers turn one compromised host into a full breach. An internal firewall limits how far they can pivot.

Mind your rule order and logging. Firewalls evaluate rules top to bottom. Put specific allow rules before broad deny rules, and enable logging so you can see what's being blocked. Logs are how you notice someone probing your server before they get in.

Key Takeaway: A default-deny firewall with explicit allow rules is the baseline. Internal servers need them too—lateral movement is the attacker's best friend.


3. Apply Security Updates Promptly

Unpatched vulnerabilities are involved in 60% of breaches. That statistic should be enough on its own, but the timing makes it worse: Palo Alto Networks Unit 42 reports the median time to exploit a vulnerability after publication is just 15 days. You have roughly two weeks between "patch available" and "weaponized exploit in the wild."

Enable automatic security updates. On Debian and Ubuntu, unattended-upgrades handles this:

sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades

Configure it to apply security updates automatically while leaving feature updates for manual review. On RHEL, dnf-automatic does the same.

Test in staging first. Automatic updates are safe for security patches in most environments, but "most" isn't "all." If you run a production service with tight uptime requirements, test patches in a staging environment before they hit production. The exception is critical remote-code-execution patches—those should go everywhere immediately.

Don't reboot-and-forget. Kernel updates require a reboot to take effect. Track which of your servers are running outdated kernels by comparing uname -r against the installed package version. Tools like needrestart can flag this automatically.

Key Takeaway: Patch fast. The 15-day median exploit window is shorter than most organizations' patch cycles. Automate security updates and test in staging where uptime matters.


4. Remove Unnecessary Services and Software

Every package on your system is code that could contain a vulnerability. Every running service is a potential entry point. The fewer you have, the smaller your attack surface.

Audit what's listening. Start with:

sudo ss -tulpn

This shows every listening TCP and UDP port, along with the process behind it. If you see something you don't recognize, investigate before you dismiss it.

Uninstall what you don't use. Default installations often ship with services you'll never touch—print servers, Bluetooth daemons, sample web apps. Remove them:

sudo apt remove package-name
sudo apt autoremove

Disable unnecessary systemd services. Even if a service isn't listening on a port, it's still running code. Check what's enabled at boot:

systemctl list-unit-files --state=enabled

Disable anything you don't need with systemctl disable --now service-name.

Beware default installations. Cloud images and distro installers often include convenience packages that make sense for a desktop but not a server. Treat every default as a liability until proven otherwise.

Key Takeaway: Attack surface reduction is quiet, unglamorous, and highly effective. Audit listening ports, remove unused packages, and disable services you don't need.


5. Implement Mandatory Access Control (MAC)

Standard Linux permissions (owner, group, others) are discretionary access control—they determine what users can do. Mandatory Access Control goes further: it defines what processes are allowed to do, regardless of which user runs them.

SELinux vs. AppArmor. SELinux was developed by the NSA and is the default on RHEL, Fedora, and CentOS. It's powerful and granular, but has a reputation for being difficult—largely because denials are cryptic until you learn to read them. AppArmor is the default on Ubuntu and SUSE. It's path-based rather than label-based, which makes it easier to understand and configure.

Both are effective. Use whichever your distribution ships with.

Enable SELinux in enforcing mode. Check the current status:

getenforce

If it says Permissive or Disabled, switch it to Enforcing in /etc/selinux/config. Then learn to troubleshoot denials with ausearch and audit2allow. Most denials are configuration issues, not bugs—a properly configured web server shouldn't trigger SELinux denials in normal operation.

Use AppArmor profiles. AppArmor ships with profiles for common services like nginx, apache2, and mysql. Check which are loaded with aa-status and put any unconfined services into complain mode first to see what they'd need, then enforce.

Why it matters. If an attacker compromises your web server process, MAC confines them. They can't read /etc/shadow, they can't write to /usr/bin, and they can't pivot to other services. The blast radius shrinks dramatically.

Key Takeaway: MAC limits damage after a compromise. It's not optional hardening—it's the difference between a contained incident and a full system takeover.


6. Enforce Least Privilege for Users and Processes

The principle of least privilege means every user and process gets exactly the permissions it needs—and nothing more. It's the single most overlooked security practice in server administration.

Create separate user accounts. Never share accounts, and never do daily work as root. Each person gets their own login, which makes auditing meaningful and revocation simple.

Configure sudo with granular permissions. Don't hand out blanket ALL=(ALL) ALL access. Use /etc/sudoers.d/ to define specific commands:

deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx

This lets the deployment user restart nginx without giving them root. Use visudo to edit sudoers files—it validates syntax before saving. A broken sudoers file can lock you out entirely.

Restrict cron jobs and SUID binaries. Cron jobs run with the privileges of their owner. If a non-privileged user can write to a cron directory, they can escalate. Similarly, SUID binaries run with the file owner's privileges—find them with:

find / -perm -4000 -type f 2>/dev/null

Audit the results and remove SUID from anything that doesn't need it.

Audit accounts regularly. Check /etc/passwd for accounts with shells that shouldn't have them. Look for unused accounts and disable them. Set password expiration policies with chage. Review group memberships—especially sudo, wheel, and docker (which is effectively root).

Key Takeaway: Least privilege is a mindset, not a checkbox. Separate accounts, granular sudo, restricted cron, and regular audits keep the blast radius small.


7. Monitor, Audit, and Back Up

You can't defend what you can't see. Monitoring, auditing, and backups turn "we might have been breached" into "we know exactly what happened and can recover."

Set up auditd. Auditd tracks system calls and file access. Watch critical files like /etc/passwd, /etc/shadow, and /etc/sudoers:

-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers

Any modification gets logged with the user, process, and timestamp. This is how you detect a compromised account changing its own password or adding a new sudoer.

Centralize logs. Local logs disappear if the server is compromised—attackers delete them first. Ship logs to a remote syslog server or SIEM so you have an independent record. Even a simple rsyslog forward to another host is better than nothing.

Use intrusion detection. AIDE and Tripwire create cryptographic hashes of your files and alert when they change. Run AIDE nightly and review the reports. Unexpected changes to system binaries are a red flag.

Back up regularly—and test restores. A backup you've never restored is a hope, not a plan. Follow the 3-2-1 rule: three copies, two media types, one offsite. Store at least one copy offline so ransomware can't encrypt it.

Encrypt data at rest. Full disk encryption (LUKS) protects data if a drive is stolen or a VM image is exfiltrated. Secure Boot ensures the bootloader hasn't been tampered with. Both matter more in cloud environments than people assume.

Key Takeaway: Monitoring detects breaches; backups recover from them. Centralize logs off-host, audit critical files, and test your restores.


Bonus: Kernel Hardening with sysctl

The Linux kernel exposes hundreds of tunable parameters through sysctl. A handful of them materially improve network security.

Add these to /etc/sysctl.d/99-hardening.conf:

# Disable IP forwarding (unless you're a router)
net.ipv4.ip_forward = 0

# Enable SYN cookies to mitigate SYN flood attacks
net.ipv4.tcp_syncookies = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Restrict kernel pointer exposure
kernel.kptr_restrict = 2

# Restrict dmesg to root
kernel.dmesg_restrict = 1

# Disable source routing
net.ipv4.conf.all.accept_source_route = 0

Apply with sudo sysctl --system. For a comprehensive baseline, the CIS Benchmarks provide distribution-specific sysctl recommendations that go much deeper.

Key Takeaway: Kernel hardening is a one-time configuration with lasting benefits. Start with SYN cookies, disabled IP forwarding, and restricted kernel pointers, then follow CIS Benchmarks for your distro.


Conclusion: Your Hardening Journey

Seven steps, plus a kernel bonus. None of them are exotic. All of them are within reach of anyone comfortable with a terminal.

To recap:

  1. Lock down SSH — no root login, keys only, Fail2ban
  2. Configure a firewall — default deny, explicit allows, internal servers included
  3. Patch promptly — automate security updates, test in staging
  4. Remove unnecessary services — audit ports, uninstall unused packages
  5. Implement MAC — SELinux or AppArmor in enforcing mode
  6. Enforce least privilege — separate accounts, granular sudo, regular audits
  7. Monitor, audit, and back up — auditd, centralized logs, tested restores

Hardening isn't a one-time project. It's a practice. New vulnerabilities appear weekly, configurations drift, and staff change. The servers that stay secure are the ones where someone is paying attention.

Once you've worked through this list, the next steps are penetration testing (to verify your assumptions) and compliance audits (to satisfy regulators and customers). Both are worth the investment—but only after the basics are solid.

Key Takeaway: Security is a process, not a product. Implement these seven steps, monitor continuously, and revisit your configuration regularly.


FAQ

Why should I disable root SSH login? Because root is the one username attackers always know exists. Disabling direct root login forces them to guess both a username and a key or password, which dramatically reduces the effectiveness of automated brute-force attacks. Use sudo for administrative tasks instead.

How often should I update my Linux server? Security updates should be applied automatically, ideally within 24–48 hours of release. The median exploit window is 15 days, so anything slower than a week is gambling. Feature updates can follow a slower, tested cadence.

What is the best way to secure SSH? Key-based authentication with password authentication disabled, root login disabled, and Fail2ban watching for malicious behavior. Changing the default port reduces noise but isn't a substitute for the first three.

Do I need a firewall if I have a router? Yes. Your router protects the network perimeter, not individual hosts. A compromised internal machine can reach your server freely unless the server itself has a firewall. Defense in depth means every layer enforces its own rules.

What is the difference between SELinux and AppArmor? SELinux uses label-based access control and is default on RHEL-family systems. AppArmor uses path-based profiles and is default on Ubuntu and SUSE. Both provide mandatory access control; AppArmor is generally easier to learn, while SELinux is more granular.

How can I monitor my server for security incidents? Start with auditd for file and syscall monitoring, ship logs to a remote syslog server or SIEM, and run AIDE or Tripwire nightly to detect unauthorized file changes. Review reports regularly—monitoring only works if someone reads the output.

Are automatic updates safe? For security patches, generally yes. For feature updates, test in staging first. The risk of an unpatched vulnerability almost always exceeds the risk of a stable security patch, but production environments with strict uptime requirements should validate before deploying.

What is the principle of least privilege? Every user, process, and service should have exactly the permissions needed to do its job—and nothing more. It limits what an attacker can do after compromising an account or process.

How do I secure data at rest? Full disk encryption (LUKS) protects data if a drive or VM image is stolen. Secure Boot ensures the bootloader is untampered. Combine both for defense in depth, and encrypt backups as well.

Why are backups important for security? Because some attacks—ransomware especially—can't be prevented, only recovered from. A tested, offline backup is your last line of defense. Without one, a breach becomes a permanent data loss event.


Ready to harden your Linux server? Download our free 7-step checklist PDF and get a printable quick-reference guide. For hands-on help, consider a professional security audit or explore advanced tools like Ansible for automated hardening.