Installing a Linux server is only the first step. Before exposing applications to users or to the Internet, the operating system should be reviewed and hardened so that unnecessary services, weak authentication methods and excessive privileges do not become easy attack paths.
This guide presents a practical post-installation hardening process for Linux servers running distributions such as Ubuntu Server, Debian, AlmaLinux and Rocky Linux.
The goal is not to turn a server into an unusable fortress. Good hardening reduces unnecessary exposure while keeping the system maintainable and recoverable.
Start with a fully updated system
Security hardening on top of an outdated operating system makes little sense. Apply available updates before changing security policy.
Ubuntu and Debian
apt update
apt full-upgrade
AlmaLinux and Rocky Linux
dnf update
If the kernel, OpenSSL, systemd or other critical components were updated, reboot before continuing.
reboot
Check the current operating system state
uname -r
cat /etc/os-release
uptime
systemctl --failed
A new server should not already contain unexplained failed services or configuration errors.
Inventory listening ports
One of the first questions during hardening should be simple: what is currently listening on the network?
ss -lntup
Every listening socket should correspond to a service that the server genuinely needs.
For example:
22/tcp SSH
80/tcp HTTP
443/tcp HTTPS
If an unexpected database, RPC or development service is listening on all interfaces, investigate before exposing the server to an untrusted network.
Disable unnecessary services
systemctl list-unit-files \
--type=service \
--state=enabled
If a service is confirmed unnecessary:
systemctl disable --now SERVICE
Do not blindly disable services because their names are unfamiliar. Understand dependencies first.
Create a dedicated administrative account
Routine administration should not require direct root login.
Ubuntu / Debian
adduser adminuser
usermod -aG sudo adminuser
AlmaLinux / Rocky Linux
useradd -m adminuser
passwd adminuser
usermod -aG wheel adminuser
Test privilege escalation before closing the current root session:
su - adminuser
sudo whoami
Use SSH public-key authentication
SSH keys are preferable to relying exclusively on reusable passwords.
Generate an Ed25519 key on the administrator workstation:
ssh-keygen -t ed25519
Copy it to the server:
ssh-copy-id adminuser@server-ip
Open a second session and verify key authentication before disabling passwords.
Harden sshd
OpenSSH configuration is commonly stored in:
/etc/ssh/sshd_config
/etc/ssh/sshd_config.d/
A reasonable baseline can include:
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no
Only disable password authentication after public-key access has been tested.
Validate the configuration:
sshd -t
If the command produces no output, the configuration syntax is valid.
Changing the SSH port is not a primary security control
Moving SSH away from TCP port 22 can reduce automated scanning noise, but it does not provide meaningful protection against a determined attacker.
Use strong authentication, firewall restrictions and monitoring as the real controls.
Configure a host firewall
A secure default is to deny unsolicited inbound connections and explicitly allow required services.
UFW on Ubuntu
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw enable
ufw status verbose
firewalld on AlmaLinux / Rocky Linux
systemctl enable --now firewalld
firewall-cmd --permanent \
--add-service=ssh
firewall-cmd --reload
firewall-cmd --list-all
Restrict SSH by source network when possible
If administration is performed through a VPN or a known management network, there is often no reason to expose SSH to every address on the Internet.
Restricting management access is one of the simplest ways to reduce exposure.
Install Fail2ban where appropriate
Fail2ban can monitor authentication logs and temporarily block addresses generating repeated failures.
Ubuntu / Debian
apt install fail2ban
systemctl enable --now fail2ban
Check status:
fail2ban-client status
For SSH:
fail2ban-client status sshd
Fail2ban is a useful secondary control, but it does not replace proper SSH authentication or firewall policy.
Keep SELinux enabled
AlmaLinux and Rocky Linux normally use SELinux.
getenforce
sestatus
The expected production state is typically:
Enforcing
If an application is blocked, investigate the policy violation rather than automatically disabling SELinux.
ausearch -m AVC -ts recent
Keep AppArmor enabled
Ubuntu commonly relies on AppArmor for mandatory access control.
aa-status
Applications should not automatically be placed into unrestricted mode simply because a profile needs adjustment.
Automatic security updates
Security updates need an explicit operational policy.
On Ubuntu and Debian:
apt install unattended-upgrades
Check the relevant configuration under:
/etc/apt/apt.conf.d/
Automation is useful, but important production servers still require monitoring, maintenance windows and recovery planning.
Audit user accounts
getent passwd
Review interactive users:
awk -F: '$7 !~ /(nologin|false)$/ {print $1,$7}' /etc/passwd
Remove old accounts only after verifying ownership of files, scheduled jobs and application dependencies.
Audit privileged groups
Ubuntu / Debian
getent group sudo
AlmaLinux / Rocky Linux
getent group wheel
Administrative access should be granted deliberately, not accumulated indefinitely.
Review sudo configuration
visudo
Additional rules may exist in:
/etc/sudoers.d/
Avoid unrestricted NOPASSWD: ALL rules unless there is a well-understood automation requirement.
Find SUID and SGID files
Setuid binaries execute with the privileges of their owner and deserve periodic review.
find / -xdev -type f \
\( -perm -4000 -o -perm -2000 \) \
-print 2>/dev/null
Many legitimate system utilities use these bits, so the purpose is inventory and anomaly detection, not blindly removing them.
Look for world-writable files
find / -xdev -type f \
-perm -0002 \
-print 2>/dev/null
Investigate unexpected results, especially in application and configuration directories.
Protect application secrets
Database passwords, API credentials, private keys and tokens should not be stored in files readable by every local user.
chmod 600 application.env
chown appuser:appuser application.env
Never solve permission problems with chmod 777
A recursive 777 permission change usually creates a security problem instead of solving an application design problem.
Use:
ls -la
stat FILE
namei -l /path/to/file
and determine the correct owner, group and required permission set.
Database exposure
A database should generally not listen on every network interface unless remote access is explicitly required.
ss -lntup | grep -E '3306|5432'
For an application and database running on the same machine, loopback-only access can be preferable.
127.0.0.1
Audit scheduled tasks
crontab -l
ls -la /etc/cron.d/
ls -la /etc/cron.daily/
Also review systemd timers:
systemctl list-timers --all
Unexpected scheduled execution is important during both hardening and incident response.
Install auditd where detailed audit trails are required
Ubuntu / Debian
apt install auditd
AlmaLinux / Rocky Linux
dnf install audit
Enable it:
systemctl enable --now auditd
Audit rules should be designed around what actually matters in the environment rather than collecting unlimited noise.
Review authentication logs
SSH activity can be inspected with systemd:
journalctl -u ssh
journalctl -u sshd
Traditional files may include:
/var/log/auth.log
/var/log/secure
Check recent login activity
last
lastlog
Unexpected login sources should be investigated.
Kernel network hardening
Some servers benefit from explicit sysctl hardening, but values should be chosen based on server role rather than copied blindly.
Inspect current settings:
sysctl -a
Persistent local configuration can be placed under:
/etc/sysctl.d/
Do not enable IP forwarding unless required
sysctl net.ipv4.ip_forward
A normal application server usually does not need to act as a router.
VPN gateways, container hosts and routers are obvious exceptions.
Backups are part of security
Hardening reduces risk, but it cannot eliminate compromise, operator mistakes or storage failure.
A secure server still needs reliable backups.
- keep multiple backup generations;
- store at least one copy away from the server;
- protect backup credentials;
- monitor backup jobs;
- test restore procedures.
Monitor the server
Security controls that nobody monitors provide limited value.
Useful signals include:
- failed authentication attempts;
- unexpected listening ports;
- disk usage;
- filesystem inode usage;
- service failures;
- package update status;
- certificate expiration;
- backup failures;
- CPU and memory anomalies.
Useful periodic checks
systemctl --failed
ss -lntup
df -h
df -i
free -h
journalctl -p warning -b
last
fail2ban-client status
Do not expose management panels directly if you do not need to
Hypervisors, storage management interfaces and application control panels should preferably be reachable through trusted management networks or VPN access rather than being universally exposed.
Use a VPN for administrative access
A private management VPN can significantly reduce the number of administrative services exposed publicly.
WireGuard is a particularly useful option because of its simple configuration and small attack surface.
Server hardening checklist
- Install all available security updates.
- Reboot after critical kernel updates.
- Review all listening ports.
- Disable unnecessary services.
- Create a dedicated administrator account.
- Configure SSH public-key authentication.
- Disable direct root SSH access.
- Disable SSH passwords after testing keys.
- Enable a host firewall.
- Restrict SSH source networks where practical.
- Install Fail2ban where useful.
- Keep SELinux or AppArmor enabled.
- Configure a security-update policy.
- Review local users.
- Review sudo/wheel membership.
- Review SUID and SGID binaries.
- Investigate world-writable files.
- Protect application secrets.
- Avoid chmod 777.
- Restrict database listeners.
- Review cron jobs and systemd timers.
- Configure audit logging where needed.
- Review login history.
- Monitor logs and service health.
- Configure off-host backups.
- Test recovery.
Hardening without breaking the server
The biggest mistake in server hardening is applying large lists of configuration changes without understanding what they affect.
Make changes incrementally.
- change one control;
- validate configuration;
- test application functionality;
- keep an existing administrative session open;
- document the change;
- only then continue.
Final thoughts
Linux server security is not a one-time script. Hardening establishes a baseline, but security depends on continuing maintenance.
Patch regularly, keep administrative access restricted, understand every exposed service, monitor authentication activity, protect backups and periodically review the server against its intended role.
A server with fewer services, clear permissions, strong authentication and a tested recovery plan is easier to defend and easier to operate.
The best security baseline is one that administrators understand well enough to maintain consistently.