# VPS Hardening & Best Practices

It focuses on practical, executable steps, including system hardening, SSH security, firewall configuration, intrusion prevention, and monitoring. All instructions are tested on Debian-based systems (e.g., Ubuntu), with emphasis on clarity and maintainability.

For Advanced Hardening in an automated script, take a look at: [ANyONe-secure-vm-hardening](https://github.com/ekisanon-anyone/ANyONe-secure-vm-hardening) script created by a dedicated member of the Anyone Community.

#### Let's begin!

## System Hardening and Updates

### :small\_blue\_diamond:Full system update

Keeping your system fully updated ensures your protection against known vulnerabilities and that all installed packages operate with the latest security patches.&#x20;

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

{% hint style="info" %}
To enable automatic System Updates: \<add instructions or link here>
{% endhint %}

### :small\_blue\_diamond:Disable Unnecessary Services

Reducing your system's attack surface starts with turning off services that aren't needed. \
Unused services may expose ports or run vulnerable software unnecessarily.

Audit running services and disable those not in use:

```bash
sudo systemctl list-units --type=service --state=running
```

Then disable with this command:

```bash
sudo systemctl disable --now <service-name>
```

#### Services you can usually disable or mask on a server operating as a relay

<table data-header-hidden><thead><tr><th width="191.88885498046875">Service</th><th>Purpose</th></tr></thead><tbody><tr><td><strong>Service</strong></td><td><strong>Purpose</strong></td></tr><tr><td><code>packagekit.service</code></td><td>Manages automatic updates graphically; not needed for headless CLI-based VPS.</td></tr><tr><td><code>snapd.service</code></td><td>Snap uses additional background daemons and mounts. It’s often unwanted bloat on servers. (Remember to <code>sudo apt purge snapd</code>)</td></tr><tr><td><code>snap.canonical-livepatch.canonical-livepatchd.service</code></td><td>Livepatching isn’t typically used on minimal or ephemeral servers. (It's usually removed by purging snapd)</td></tr><tr><td><code>multipathd.service</code></td><td>Multipath is for SAN and storage setups. Not needed on cloud VPS or simple disk configs.</td></tr></tbody></table>

#### Services you should usually keep

<table data-header-hidden><thead><tr><th width="261.9999694824219"></th><th></th></tr></thead><tbody><tr><td><strong>Service</strong></td><td><strong>Purpose</strong></td></tr><tr><td><code>dbus.service</code></td><td>Required by some utilities and desktop components. Harmless and often needed by other services.</td></tr><tr><td><code>getty@tty1.service</code></td><td>Provides login on local TTY; not harmful,  but optional to disable in headless environments.</td></tr><tr><td><code>networkd-dispatcher.service</code></td><td>Helps with DHCP Netplan events; required on Netplan-managed systems.</td></tr><tr><td><code>polkit.service</code></td><td>Used for user privilege escalation. Needed if you use desktop tools or sudo based GUI operations; may be safe to disable on hardened CLI-only systems.</td></tr><tr><td><code>ssh.service</code></td><td>Keep for remote access.</td></tr><tr><td><code>systemd-networkd.service</code></td><td>Manages network interfaces; critical for connectivity on minimal systems.</td></tr><tr><td><code>systemd-resolved.service</code></td><td>Handles DNS; often required unless using custom resolvers or DNS tools.</td></tr><tr><td><code>systemd-timesyncd.service</code></td><td>Keeps system clock accurate (important for anon).</td></tr><tr><td><code>systemd-journald.service</code></td><td>System logging; required!</td></tr><tr><td><code>systemd-logind.service</code></td><td>Handles user logins and sessions; usually fine to leave.</td></tr><tr><td><code>systemd-udevd.service</code></td><td>Manages device nodes; needed.</td></tr><tr><td><code>user@1000.service</code></td><td>Your user session; leave it alone.</td></tr></tbody></table>

***

## SSH Security

SSH is the lifeline of remote management, securing it is critical.

### :small\_blue\_diamond:Change Default SSH Port

Changing the SSH port from the default `22` to a custom value (e.g., `52231`) helps reduce exposure to automated scans and brute-force attacks. While not truly secure, it adds a layer of obscurity that can reduce low-effort intrusion attempts.

Edit `sshd_config`

```bash
sudo nano /etc/ssh/sshd_config 
```

Look for **`#Port 22`** and change it to to anything else, and for the sake of this example we just randomly selected `52231`:

```ini
Port 52231
```

Then restart the SSH service.

```bash
sudo systemctl restart sshd.service
```

### :small\_blue\_diamond:Enable Login Banners

Login banners warn users that the system is monitored and restricted. These messages can serve legal or policy purposes by clearly stating that unauthorized access is prohibited.

Edit `/etc/issue.net` with a warning message.

```bash
sudo nano /etc/issue.net
```

Replace the contents and save it with something like:

```ini
ALERT! You are entering a secured area! Your IP, Login Time, and Username have been noted and have been sent to the server administrator!
This service is restricted to authorized users only. All activities on this system are logged.
Unauthorized access will be fully investigated and reported to the appropriate law enforcement agencies.
```

Edit `/etc/ssh/sshd_config` and look for the banner option.

```bash
sudo nano /etc/ssh/sshd_config
```

```ini
# no default banner path
#Banner none
```

Set it to the updated `issue.net` file.

```ini
Banner /etc/issue.net
```

Restart the SSH Service.

```bash
sudo systemctl restart sshd.service
```

{% hint style="info" %}
Suggestion provided on AskUbuntu.com:\
<https://askubuntu.com/questions/420375/how-to-add-legal-banner-in-etc-issue-and-etc-issue-net-in-ubuntu>
{% endhint %}

### :small\_blue\_diamond:Disable Root Login (Recommended)

Disabling direct root login via SSH helps prevent attackers from brute-forcing the root account. Instead, users should authenticate with non-root accounts and escalate privileges securely using `sudo`.

{% hint style="info" %}
Read more about typical SSH Key Scanning at:\
<https://www.ssh.com/blog/ssh-key-scan-attack-honeypot>
{% endhint %}

If you've followed the guide so far you already know how to edit `/etc/ssh/sshd_config`.\
\
Set:

```ini
PermitRootLogin no
```

### :small\_blue\_diamond:Set Up SSH Key Authentication (Recommended)

SSH key authentication replaces passwords with cryptographic key pairs, significantly reducing the risk of brute-force attacks. It’s a foundational best practice for secure remote access.

Generate key pair (on a local machine):

```bash
ssh-keygen -t ed25519
```

{% hint style="success" %}
Generating public/private ed25519 key pair.\
Enter file in which to save the key (/root/.ssh/id\_ed25519): `[Press ENTER]`\
Created directory '/root/.ssh'.\
Enter passphrase (empty for no passphrase): `[Press ENTER]`\
Enter same passphrase again: `[Press ENTER]`\
\
Your identification has been saved in /root/.ssh/id\_ed25519\
Your public key has been saved in /root/.ssh/id\_ed25519.pub
{% endhint %}

Copy public key to the server:

```bash
ssh-copy-id -p 52231 user@server_ip
```

{% hint style="success" %}
INFO: Source of key(s) to be installed: "/root/.ssh/id\_ed25519.pub"\
The authenticity of host 'server\_ip (server\_ip)' can't be established.\
ECDSA key fingerprint is SHA256:\<redacted>.\\

\
Are you sure you want to continue connecting (yes/no/\[fingerprint])? yes\
\
INFO: attempting to log in with the new key(s), to filter out any that are already installed\
The authenticity of host 'server\_ip (server\_ip)' can't be established.\
ECDSA key fingerprint is SHA256:\<redacted>.\\

\
Are you sure you want to continue connecting (yes/no/\[fingerprint])? yes\\

\
INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys\
\
ALERT! You are entering a secured area! Your IP, Login Time, and Username have been noted and have been sent to the server administrator!\
This service is restricted to authorized users only. All activities on this system are logged.\
Unauthorized access will be fully investigated and reported to the appropriate law enforcement agencies.\\

\
user\@server\_ip's password:

\
Number of key(s) added: 1

Now try logging into the machine, with: "ssh -p '52231' 'user\@server\_ip'"\
and check to make sure that only the key(s) you wanted were added.
{% endhint %}

Try to log in to the server from your local machin with the new key:

```bash
ssh -p '52231' 'user@server_ip
```

On the server, disable password authentication in `sshd_config`:

```ini
# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
```

Set:

```ini
PasswordAuthentication no
```

{% hint style="info" %}
You can find detailed descriptions of the commands used for this tutorial at:

<https://www.ssh.com/academy/ssh/keygen>

<https://www.ssh.com/academy/ssh/copy-id>

<https://www.ssh.com/academy/ssh/sshd_config>
{% endhint %}

***

## Firewall & Network Protection

### :small\_blue\_diamond:UFW Firewall

UFW (Uncomplicated Firewall) makes it easy to manage iptables and control which services are exposed to the internet. Setting a default-deny policy and allowing only essential ports helps contain threats and limit exposure.

```bash
sudo apt install ufw -y
```

#### Default Policy

```bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
```

#### Allow Essential Ports

```bash
sudo ufw allow 52231/tcp    # Custom SSH
sudo ufw allow 53/udp       # DNS (if applicable)
sudo ufw allow 9001/tcp     # Example: anon service
```

Enable the service.

```bash
sudo ufw enable
```

To check the status of UFW:

```bash
sudo ufw status verbose
```

{% hint style="info" %}
When running the [installation script](https://docs.anyone.io/relay/start/install-anon-on-linux), UFW is offered as an option to enable access for SSH and ORPort.\
\
Read more about UFW on the official Ubuntu documentation:\
<https://help.ubuntu.com/community/UFW>
{% endhint %}

***

## Intrusion Detection and Abuse Prevention

Automated protection against brute-force and scanning behavior.

### :small\_blue\_diamond:Fail2Ban

Fail2Ban monitors system logs for failed login attempts or suspicious behavior, then bans the source IP using firewall rules. It's highly effective for deterring brute-force attacks against SSH and other services.

{% hint style="info" %}
Find the official documentation and repository for Fail2ban at:

<https://github.com/fail2ban/fail2ban>
{% endhint %}

#### Install Fail2Ban.

```bash
sudo apt install fail2ban -y
```

#### Configure Fail2ban

Instead of modifying the default `jail.conf`, create a local configuration.

```bash
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
```

In the `[DEFAULT]` section, adjust the following parameters as needed:

```ini
[DEFAULT]
bantime  = 10m
findtime = 10m
maxretry = 5

```

{% hint style="info" %}
`bantime` - Duration an IP is banned (e.g., 10 minutes).

`findtime` -  Time window to count failures (e.g., 10 minutes).

`maxretry` - Number of allowed failures before a ban (e.g., 5 attempts).
{% endhint %}

#### Enable SSH Protection

Ensure the `[sshd]` jail is enabled.

```ini
[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
```

If you've changed the SSH port (e.g., to 52231), update the `port` value accordingly.

```ini
port = 52231
```

#### Start and Enable Fail2Ban

```bash
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
```

Monitor Fail2Ban Status

Check the status of Fail2Ban and its jails.

```bash
sudo fail2ban-client status
sudo fail2ban-client status sshd
```

To unban an IP, use the `set` function:

```bash
sudo fail2ban-client set sshd unbanip <IP_ADDRESS>
```

***

## Monitoring

Basic system monitoring helps detect when something is wrong; like resources, failed services, or suspicious activity. Before it becomes a full outage or compromise.

### :small\_blue\_diamond:Install Watchdog (optional)

```bash
sudo apt install watchdog -y
sudo systemctl enable --now watchdog
```

Configure `/etc/watchdog.conf` for system checks like disk, memory, or network loss.

{% hint style="info" %}
See Ubuntu Man pages for detailed description on how to modify the configuration file for Watchdog:\
<https://manpages.ubuntu.com/manpages/xenial/man8/watchdog.8.html>
{% endhint %}
