Marcio Cunha

How to Secure a Linux Server: Essential Security Practices

Discover the definitive step-by-step guide to hardening Linux server security in the cloud or on-premise. Learn how to close unnecessary ports, configure SSH key authentication, and shield your system against intrusions.

Marcio Cunha12 min
Also available in:EspañolPortuguês
Summary
  • Disabling password login and exclusively using cryptographic keys drastically reduces brute-force intrusions.
  • Using UFW or iptables as a firewall restricts network traffic strictly to essential application ports.
  • The Fail2ban tool acts as an automated guard that temporarily blocks IP addresses exhibiting suspicious access attempts.
  • Applying regular kernel updates prevents publicly known vulnerabilities from being exploited by malicious actors.
  • The principle of least privilege ensures users and processes run only with permissions strictly necessary for operation.

The challenge of keeping a Linux server secure in the modern world

Securing a Linux server is much like shielding a digital fortress against constant threats roaming the internet. The vast majority of automated attacks are not personally targeted, but rather launched by bots scanning the network for open ports and weak passwords. In practice, this means any machine connected to the cloud without a basic defense layer will be compromised within hours. The good news is that the Linux ecosystem provides extremely powerful native tools to permanently close these security gaps.

In this article, we will explore essential practices to harden the security of your Linux server. From initial access configuration to continuous log monitoring, each step is designed to build multiple layers of protection. Even if you are not a systems engineering expert, understanding these fundamental concepts will allow you to keep your digital infrastructure robust, reliable, and immune to the vast majority of everyday automated incidents.

Replacing passwords with cryptographic keys in SSH

Secure Shell (SSH), the protocol used to access servers remotely via command line, is the primary and most important target for external attacks. By default, many systems come configured to accept standard passwords, leaving room for brute-force attacks where scripts try guessing your password millions of times. The definitive solution is to completely disable password login and use cryptographic keys—a pair of mathematically complex files where only your computer holds the private key that unlocks the server's digital lock.

To implement this change in practice, you must edit the SSH service configuration file, located at /etc/ssh/sshd_config, and change the PasswordAuthentication directive to no. Additionally, it is strongly recommended to change the default SSH port (typically 22) to a high, random port, drastically reducing the noise of malicious attempts in system logs. Below is a practical example of the essential directives that must be adjusted:

# Recommended adjustments in /etc/ssh/sshd_config
Port 22022
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

After saving the changes, it is vital to restart the service using the command sudo systemctl restart ssh before closing your current session. Test the access in a new terminal tab to ensure your private key is working correctly; otherwise, you might lock yourself out of the remote server. This simple tweak instantly eliminates over ninety percent of automated invasion attempts hitting servers exposed to the public internet.

Controlling network traffic with a restrictive firewall

A secure server exposes to the outside world only what is strictly necessary for its operation. If your machine hosts a single website, for example, it needs to accept connections on ports 80 (HTTP) and 443 (HTTPS), plus the customized SSH port. All other network traffic should be blocked by default to prevent internal service ports from becoming vulnerable to external exploits. To manage these rules simply within the Ubuntu and Debian ecosystem, we use UFW (Uncomplicated Firewall), a user-friendly interface for the Linux kernel packet filtering system.

In practice, configuring UFW requires a logical sequence of commands to set the default policy as restrictive before allowing authorized ports. The command sudo ufw default deny incoming blocks any unsolicited incoming attempts, while sudo ufw default allow outgoing allows the server to make requests to the internet, such as downloading updates. Next, we open only what is necessary with specific commands for the customized SSH port and web services:

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

With the firewall enabled via the sudo ufw enable command, the server silently rejects any network packet destined for unmapped ports. This prevents database services or administrative panels from being accidentally exposed if installed without proper local access restriction. Periodic auditing of these rules ensures that the attack surface always remains minimized to the absolute minimum.

Automatically blocking intruders with Fail2ban

Even by changing the SSH port and using cryptographic keys, malicious bots will keep trying to find loopholes by knocking on your server's door tirelessly. To mitigate this aggressive behavior without manually monitoring logs every single day, we use Fail2ban, a utility that analyzes log files in real time. When the system notices a repeated pattern of authentication failures coming from the same IP address, it automatically creates a temporary firewall rule to ban that intruder for a set period.

Installing Fail2ban on most Linux distributions is done via the default package manager, such as sudo apt install fail2ban. Program behavior is defined by configuration files located in the /etc/fail2ban/ directory, where we can create a custom file named jail.local to override factory defaults. Below is a functional configuration example to protect the SSH service against persistent brute-force attacks:

[sshd]
enabled = true
port = 22022
logpath = %(sshd_log)s
maxretry = 3
bantime = 3600
findtime = 600

In this practical example, if an IP address fails connection attempts three times (maxretry = 3) within a ten-minute window (findtime = 600), it will be blocked for a full hour (bantime = 3600). This intelligent automation relieves server resource load and discourages automated attackers who rely on tens of thousands of attempts per minute to find weak passwords. Monitoring these blocks can be done at any time using the command sudo fail2ban-client status sshd.

Keeping the system updated against known vulnerabilities

No software is completely free of security flaws, and the Linux operating system itself receives constant patches discovered by researchers and developers. When a critical vulnerability becomes public, cybercriminals rush to create scripts capable of exploiting it before administrators update their servers. Therefore, establishing a rigorous routine of operating system and installed package updates is one of the most important practices to guarantee the longevity and integrity of digital infrastructure.

In practice, updating involves two fundamental steps: synchronizing the list of available packages in official repositories and effectively applying fixes. On Debian and Ubuntu-based servers, we execute this routine by combining APT package manager update commands:

sudo apt update && sudo apt upgrade -y
sudo apt dist-upgrade -y

In mission-critical production environments where stability is absolute priority, many teams choose to automate specific security updates while keeping core packages under strict manual control. Tools like unattended-upgrades can be configured to automatically apply kernel patches and critical packages overnight, reducing exposure windows to zero-day flaws. Discipline in applying these fixes seals breaches before malicious agents can take advantage of them.

Applying the principle of least privilege to users

The principle of least privilege dictates that any user, program, or process must have only the access strictly necessary to perform its function, and absolutely nothing beyond that. In the context of a Linux server, this means you should never run everyday applications or web services using the superuser account (root), which possesses absolute power over the entire operating system. If a web application suffers a breach due to code flaws, an attacker finding the process running as root will take full control of the machine; if the process runs under a restricted user, the damage will be contained to that specific account.

To manage privileges securely, we create individual user accounts for everyone who needs server access and grant temporary administrative permissions exclusively via the sudo command. Creating a new user is done with sudo adduser username, followed by adding that user to the sudo administrative group with sudo usermod -aG sudo username. Additionally, sensitive file permissions must be rigorously audited using commands like chmod and chown, ensuring configuration files with database passwords can only be read by authorized processes.

Final Considerations

Securing a Linux server is not a one-time event that happens on installation day, but a continuous process of vigilance, maintenance, and adaptation to new threats. By implementing cryptographic key authentication, restricting network traffic with a proper firewall, automating intruder bans with Fail2ban, and keeping the system rigorously updated, you elevate your infrastructure security to a professional level. Operational discipline and strict adherence to the principle of least privilege form the foundation upon which we build truly resilient digital environments.

Remember that absolute security does not exist in software engineering; the real goal is to raise the operational cost of an attack to the point of discouraging any automated or opportunistic intruder. As your infrastructure grows, adopting automated auditing tools and file integrity monitoring will perfectly complement these foundational practices. Stay curious, study your system log behavior, and treat security as an inherent part of developing any modern architecture.