Securing a new server

A hardening checklist in the order that matters, starting with the mistakes that actually get servers taken over.

Security7 min read

On this page
  1. The checklist
  2. Credentials, first
  3. Use a key, then turn password sign-in off
  4. One password per thing, and none of them reused
  5. Change every credential that arrived with the software
  6. Then, what is listening
  7. Then, patching
  8. The updates that get skipped are the ones that matter
  9. A normal user and sudo
  10. Rate-limit the attempts
  11. The rest of the list
  12. When something is already wrong
  13. Related

A new server is reachable from the whole internet the moment it finishes building, and automated scanners find new addresses on their own, without anyone choosing you. This page is the order to work in, with the things that actually get servers compromised at the top.

Almost nothing on this list is clever. Servers are lost to a guessed password, a database left listening on a public address, or a piece of third-party code someone downloaded from a forum. Work down the list and stop worrying about the exotic attacks until the ordinary ones are closed.

The checklist

  1. Get the credentials right. Key-based SSH, password sign-in off, and no password reused from anywhere else.
  2. Close what should not be listening. Everything on a public address is a way in. Most services do not need one.
  3. Keep software patched, including the code you did not write. Automatic OS updates, and real care with game server resources and plugins.
  4. Stop signing in as root. A separate user with sudo limits what one mistake or one stolen session can do.
  5. Slow down brute force. Fail2ban and a sane SSH port policy.
  6. Have backups you have restored. The control that decides how bad the worst day is.
  7. Secure the account that controls the server. Your Clouduxe sign-in can rebuild the machine. Treat it accordingly.

Items 1 and 2 are not first because they are quick. They are first because almost every compromised server this documentation exists for was lost to one of them. A fully patched machine with a weak root password is still lost, and so is a hardened SSH configuration on a box with an unauthenticated database on a public port.

PrismFilters traffic at theports you openHost firewallDefault deny inbound, rulesonly for what you openedWhat is listeningA public address, or127.0.0.1 onlySign-inKeys only, password sign-inoffThe code you runPatched, and from sourcesyou can identifyA flood at yourgame portStoppedGuessing the SSHpasswordStoppedScanning for anexposed databaseStoppedA backdoor in aplugin youinstallednever sees itnever sees itnever sees itnever sees itStoppedA flood ata game portSSHpasswordguessingAn exposeddatabaseAbackdooredpluginPrismIn front of the server: filters traffic at theports you openStoppedunseenHost firewallDefault deny inbound, rules only for what youopenedStoppedunseenWhat is listeningA public address, or 127.0.0.1 onlyunseenSign-inKeys only, password sign-in offStoppedunseenThe code you runPatched, and from sources you can identifyStopped
Each way in is stopped by a different layer. Prism filters a flood at the ports you open, but a guessed password is ordinary traffic to an SSH port open to everyone, and keys are what stop it; a database left on a public address is closed by the firewall and again by binding it to 127.0.0.1; and a backdoored plugin arrives with you, where no network layer sees it. Illustration.

Credentials, first

Use a key, then turn password sign-in off

A password on port 22 is guessed at continuously by software that does nothing else. A key is not guessable in the same way, and once password authentication is off, the entire category of attack stops working against you rather than being slowed down.

The order matters, because doing this in the wrong order locks you out:

  1. Add your public key to the server and confirm you can sign in with it in a second terminal, without closing the first.
  2. Only then set PasswordAuthentication no in the SSH server config and reload the service.

SSH hardening has the full procedure, the exact config lines, and how to get back in if you do lock yourself out.

One password per thing, and none of them reused

The password on your server, the password on your Clouduxe account, the one on your game server's admin panel, and the one on your database are four different secrets. Reusing one means a leak anywhere becomes a break-in everywhere, and the leak is usually somewhere you do not control.

Use a password manager and generate them. A password you can remember for a service you sign into twice a year is a password worth attacking.

Change every credential that arrived with the software

Admin panels, game server frameworks, and database images ship with default or first-run credentials, and the defaults are public. Anything that came with a password you did not choose gets a new one before that service is reachable.

Then, what is listening

Every service bound to a public address is a way in, whether you meant to expose it or not. Databases, cache servers, admin panels, and management ports are regularly found open because a package's default configuration listened on all interfaces and nobody checked.

List what is actually reachable on the machine:

Shell
sudo ss -tulpn

Read the Local Address:Port column. An entry showing 0.0.0.0: or [::]: is listening on every interface, which includes your public IP. An entry showing 127.0.0.1: or [::1]: is local only and cannot be reached from outside.

For each line that is public, decide which of three things it is:

  • Something players or users need, such as your game port or a web port. Keep it, and put a rate-bounded rule in front of it. See Port rules.
  • Something only you need, such as SSH, a database, or an admin panel. Bind it to 127.0.0.1 in that service's own config and reach it over an SSH tunnel, or restrict it to your own address in the firewall.
  • Something you did not know was running. Find out what it is before you decide, and if nothing needs it, disable the service rather than only firewalling it.
Netid Local Address:Port ProcessWhat it is, and what to dotcp 0.0.0.0:25565 javaPlayers need itKeep it, with a port rule in fronttcp 0.0.0.0:22 sshdOnly you need itRestrict it to your own address in the firewalltcp 127.0.0.1:3306 mysqldLocal onlyCannot be reached from outsidetcp 0.0.0.0:6379 redis-serverOnly you need itBind it to 127.0.0.1 in its own configtcp 0.0.0.0:8000 python3Did not know it was thereFind out what it is; disable it if nothing needs ittcp 0.0.0.0:25565 javaPlayers need itKeep it, with a port rule in fronttcp 0.0.0.0:22 sshdOnly you need itRestrict it to your own address in the firewalltcp 127.0.0.1:3306 mysqldLocal onlyCannot be reached from outsidetcp 0.0.0.0:6379 redis-serverOnly you need itBind it to 127.0.0.1 in its own configtcp 0.0.0.0:8000 python3Did not know it was thereFind out what it is; disable it if nothing needsit
Example lines from sudo ss -tulpn, read by their Local Address:Port. 0.0.0.0 listens on every interface, public address included, and 127.0.0.1 is local only; each public line is one of the three kinds above. The cache on 6379 is one that does not belong there. Example output, trimmed to three columns.

Then put a host firewall in front of everything, with a default of deny inbound and explicit rules for what you opened above. The Linux firewall covers ufw on Debian and Ubuntu, and firewalld on AlmaLinux, Rocky and RHEL, including how to avoid locking yourself out.

Then, patching

Turn on automatic security updates so this is not a thing you have to remember.

On Debian and Ubuntu:

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

On AlmaLinux, Rocky and RHEL:

Shell
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic.timer

dnf-automatic downloads updates but does not install them until you set apply_updates = yes in /etc/dnf/automatic.conf. Edit that file before you consider this done.

Kernel and core library updates need a reboot to take effect. On Ubuntu, the file /run/reboot-required exists while one is pending, and Debian creates it too once update-notifier-common is installed. On the RHEL family, dnf needs-restarting -r, from the dnf-utils package, reports whether a reboot is needed.

The updates that get skipped are the ones that matter

OS packages are the half that automates itself, and they are not where most game servers are broken into. The risky code is the code you added:

  • Game server frameworks and their resources or plugins. Track upstream releases and update deliberately.
  • Anything downloaded from a forum, a Discord server, or a marketplace you cannot identify. Leaked and re-uploaded scripts are a well-known delivery route for backdoors, and the backdoor is usually the reason the copy was free. If you cannot read the source, you are trusting a stranger with root-adjacent access to your machine.
  • Web applications and their dependencies, which are exposed on a port by definition.

A normal user and sudo

Create a user for yourself, give it sudo, and stop using the root account for day-to-day work. Two things improve: a mistake in a command affects less, and your services can run as their own unprivileged users instead of as root, so a flaw in one of them does not immediately hand over the whole machine.

Never run a game server as root. See Users and permissions.

Rate-limit the attempts

With password sign-in already off, brute force against SSH cannot succeed, but it still fills your logs and burns CPU. Fail2ban watches the auth log and blocks addresses that keep failing, and it works for more than SSH: it can watch a web server or a game panel's log too. See Fail2ban.

Moving SSH to a non-standard port reduces noise in the logs. It is not security on its own, and it does not replace anything above it on this page.

The rest of the list

  • Backups you have actually restored. Everything above reduces the chance of a bad day. Backups decide how bad the bad day is. See Planning backups that actually work.
  • Your Clouduxe account. Whoever holds it can rebuild the server and read its billing history. It needs a unique password and 2FA. See Securing your Clouduxe account.
  • Know what normal looks like. Baseline your CPU, memory, and player counts now, while things are healthy. You cannot recognise the abnormal without it, and the same baseline is what you size your protection rules against.
  • Read your logs occasionally, rather than for the first time during an incident.
  • Windows servers. The same priorities apply with different tools, and the single biggest one is not leaving RDP open to the entire internet. See Hardening Remote Desktop.

When something is already wrong

If you are reading this because a server is behaving strangely rather than because it is new, stop here and go to What to do if your server is compromised. Hardening a machine that is already in someone else's hands does not remove them.

Need a server, or a hand with one?

Pick a plan to deploy a server, or open a ticket in the panel. Support runs 24/7/365.