Skip to content
L Luka Piplica
linux security sysadmin devops hardening

VPS Hardening: A Practical Guide to Securing a Linux Server

An industry-standard security blueprint for securing clean Linux installations, configuring public-key cryptography, deploying UFW firewalls, and establishing proactive threat mitigation.

L

Luka Piplica

8 min read
Retro green screen display with an Access Denied security warning message

The moment a fresh virtual private server (VPS) or cloud instance is provisioned with a public IP address, it becomes a target. Automated threat botnets constantly scan global IPv4 ranges, attempting to exploit default ports, brute-force weak credentials, and identify unpatched system services. Leaving a server running on default configurations is an open invitation to intrusion.

Establishing a solid, hardened security baseline is a vital prerequisite for any production deployment, whether hosting simple web applications, database servers, or complex API networks.

This practical guide provides a step-by-step security blueprint for hardening a clean Linux installation (specifically targeting Debian and Ubuntu distributions), configuring secure cryptographic access, deploying stateful firewalls, and implementing proactive threat mitigation workflows.


Hardening Architecture Specification

Rather than deploying complex third-party tools, effective server hardening leverages stable, built-in Linux subsystem utilities. The core defensive specifications implemented in this guide are outlined below:

Security VectorSoftware / ProtocolTechnical Objective
Operating SystemDebian / UbuntuClean base-layer Linux server environment
Package Lifecycleunattended-upgradesAutomated scheduling and execution of critical security patches
Access AuthorizationEd25519 Cryptographic KeysHigh-speed, side-channel immune asymmetric key-pair authorization
Network DefenseUFW (Uncomplicated Firewall)Strict stateful packet filtering (default-deny policy)
Host ConcealmentICMP Filter (before.rules)Drops incoming echo requests to hide active server presence from scanners

Phase 1: Package Synchronization and Patch Automation

Maintaining up-to-date software packages is the single most effective defense against known CVEs (Common Vulnerabilities and Exposures).

1. Manual System Synchronization

Begin by performing a manual update and package upgrade to ensure a clean baseline:

sudo apt update
sudo apt upgrade -y

2. Automating Critical Security Patches

For production environments, manually running system updates daily is unfeasible. We configure the system to automatically apply security updates using the unattended-upgrades utility.

Install the required package:

sudo apt install -y unattended-upgrades

To enable background updates and configure the package delivery frequency, reconfigure the package priority rules:

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

(Select Yes when prompted to enable automatic background downloading and installation of security-critical packages).

Systems Note: Depending on your service uptime guarantees, you should carefully monitor auto-upgrades. While security patches are highly critical, minor package conflicts could occasionally disrupt specialized running daemons.


Phase 2: Hardening Remote Shell Access (SSH)

Securing the Secure Shell (SSH) daemon is a core step in server hardening. By default, SSH operates on port 22 and often permits password-based authentications, leaving it vulnerable to automated brute-force attacks.

1. Directory Preparation and Security Constraints

Create a secure .ssh folder within your user directory and set strict file permission bits:

mkdir -p ~/.ssh
chmod 700 ~/.ssh

(A permission state of 700 ensures that only the directory owner has read, write, and execute permissions, preventing other local users from viewing or altering your credentials).

2. Generating Ed25519 Cryptographic Key Pairs

We replace password authentication entirely with asymmetric key-pair authentication. We select the modern Ed25519 curve algorithm because it is significantly faster than legacy RSA, features shorter key sizes, and is structurally immune to side-channel attacks.

Execute the generator command on your local client machine (Windows PowerShell, macOS, or Linux terminal):

ssh-keygen -t ed25519

Tip: Always secure your key pair with a strong passphrase. This ensures that even if your private key file is compromised, it cannot be decrypted without the passphrase.

3. Deploying the Public Key to the Remote Host

Next, copy your newly generated public key (id_ed25519.pub) to the target server's authorized_keys file.

  • Linux / macOS Client:
    ssh-copy-id -i ~/.ssh/id_ed25519.pub SERVER_USER@SERVER_IP
  • Windows PowerShell Client:
    scp $env:USERPROFILE/.ssh/id_ed25519.pub SERVER_USER@SERVER_IP:~/.ssh/authorized_keys

Once copied, verify that you can authenticate remotely without entering your account password.

4. Restricting the SSH Daemon Configuration

With keys in place, log into your server and edit the SSH server daemon configuration file to disable password logins and customize network behaviors:

sudo nano /etc/ssh/sshd_config

Incorporate or modify the following directives:

  1. Custom Port Allocation: Change the default SSH port to a custom high port (e.g., 4723) to avoid automated brute-force bot scanners targeting Port 22:
    Port 4723
  2. IP Family Restrictions: Limit the daemon to listen strictly on IPv4 (replacing the default any directive):
    AddressFamily inet
  3. Disable Direct Root Logins: Prevent direct administrative root authentication, forcing users to connect via standard accounts and escalate privileges via sudo:
    PermitRootLogin no
  4. Enforce Key-Only Logins: Disable weak password-based logins entirely:
    PasswordAuthentication no

Save the file and restart the SSH system daemon to apply the configuration:

sudo systemctl restart sshd

Warning: Do not close your active terminal session after restarting the SSH daemon. Open a new terminal window and verify that you can connect successfully using your custom port and private key. This prevents you from locking yourself out if you made a typo in the config file.


Phase 3: Deploying a Stateful Firewall (UFW)

A stateful firewall monitors active connections and blocks unauthorized network traffic before it reaches your applications.

1. Auditing Active Network Sockets

Before blocking traffic, check which network sockets are actively listening on your host:

sudo ss -tupln

(Analyze the output to ensure you do not block ports used by critical active services like databases, web servers, or key-value caches).

2. Enforcing Firewall Rules

Install the Uncomplicated Firewall (UFW) utility:

sudo apt install -y ufw

Establish your target port authorization rules. Since we changed our SSH port to 4723, we authorize inbound TCP traffic on that specific custom port:

sudo ufw allow 4723/tcp

Enable the firewall and check its status:

sudo ufw enable
sudo ufw status verbose

(The status output should confirm a default-deny inbound policy, with Port 4723 authorized for TCP traffic).


Phase 4: Defensive Network Concealment (ICMP Dropping)

By default, Linux servers respond to Internet Control Message Protocol (ICMP) echo requests (more commonly known as pings). While useful for diagnosing network connectivity, hackers and botnets use ping sweeps to discover active online hosts and map target network ranges.

Disabling ping responses makes your server look like a dead IP address to automated sweeps, reducing target profile visibility.

  1. Open the core UFW pre-routing rules file:
    sudo nano /etc/ufw/before.rules
  2. Locate the section labeled # ok icmp codes for INPUT.
  3. Directly at the top of this block (before any matching accept rules), insert the drop directive:
    -A ufw-before-input -p icmp --icmp-type echo-request -j DROP
  4. Save the changes and restart the server to apply the packet routing rules:
    sudo reboot now

Once the system completes its reboot, attempt to ping the host from an external machine. The request will time out, indicating the server is successfully hiding its network presence.


Technical Glossary: Hardening Lexicon

Term / UtilityTechnical OverviewVisual & Security Benefit
unattended-upgradesAutomated security patch managerPeriodically retrieves and applies critical CVE patches in the background, keeping dependencies secure without manual admin oversight.
Ed25519 KeysAsymmetric elliptic curve algorithmProvides high-security access based on elliptic curve cryptography, offering faster speeds and a smaller key footprint than legacy RSA.
sshd_configSSH daemon system configuration fileThe central file used to restrict access policies, customize ports, enforce key-only logins, and disable direct root access.
ssSocket statistics utilityA modern command-line tool used to audit active TCP/UDP ports, listening sockets, and process IDs.
UFWUncomplicated Firewall wrapperA command-line frontend to iptables, providing an easy-to-use interface to manage stateful packet filtering policies.
ICMP Echo RequestInternet Control Message Protocol packetA diagnostic packet sent to verify a host's network presence. Dropping these packets hides your server from automated scanners.

Conclusion & Architectural Retrospective

Hardening a Linux server is an ongoing process of threat modeling and reducing your system's attack surface. While no server is entirely unhackable, deploying these baseline defense-in-depth principles—enforcing key-only SSH access, automating patches, configuring a stateful firewall, and dropping network ping requests—significantly raises the barrier to entry, protecting your host and services from automated bot sweeps and brute-force campaigns.

Implementing a robust, defense-in-depth configuration on public-facing servers demonstrates how systematic policy enforcement and proactive threat modeling form the cornerstone of secure cloud engineering and infrastructure reliability.

Back to Blog
Share:

Follow along

Stay in the loop — new articles, thoughts, and updates.