A server with a public IP address receives automated login attempts within minutes of being switched on. You don't need to be an interesting target: automated programs continuously scan the Internet looking for weak passwords and unpatched services. The first steps after creating a server are therefore the most important ones, and they take less than an hour. The examples below refer to a Debian- or Ubuntu-based distribution.
1. Update the system straight away
The image the server is created from may be weeks or months old. The first command is always a full package upgrade:
apt update && apt full-upgrade -y
If the kernel is updated, a reboot is needed for the new version to take effect.
2. Create a personal user
Working as root makes every mistake potentially destructive and leaves unclear traces in the logs. You create a user with your own name and allow it to use sudo for administrative operations:
adduser mario
usermod -aG sudo mario
If several people manage the server, each must have their own user. That way you know who did what, and when someone leaves the project you just disable their account.
3. Log in with SSH keys
Key-based authentication is much more robust than a password. On your computer you generate a key pair, preferably of type Ed25519, and copy the public key to the server:
ssh-keygen -t ed25519
ssh-copy-id mario@server-address
The private key stays on your computer, protected by a passphrase. Many providers let you add the public key when the server is created, and that is the best option.
4. Close the weakest entry points
Once you have verified that key login works, from a second open session, you edit the SSH server configuration in /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
Then you reload the service. Keeping the original session open until you have verified the new login prevents you from locking yourself out because of a typo.
Moving SSH from port 22 to another port reduces noise in the logs, but it is not a real security measure: with passwords disabled, automated attempts fail anyway.
5. Enable a firewall
The principle is simple: everything closed, except what is needed. With ufw, Ubuntu's uncomplicated firewall:
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
If the provider also offers a network firewall outside the server, it is worth using as well: it filters traffic before it reaches the machine and stays active even if the internal configuration is changed by mistake.
A warning for Docker users: ports published by containers can bypass ufw rules, because Docker manages its own network rules directly. For internal services, such as a database, publish the port only on the local address or don't publish it at all.
6. Automatic security updates
Nobody remembers to update a server every week. The unattended-upgrades package automatically installs security fixes:
apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades
You can configure it to reboot the server at a set time when an update requires it. For the most critical servers you may prefer a notification and a manual reboot, but the choice should be made deliberately.
7. Check what is listening
The ss -tulpn command shows every service accepting connections and on which address. Every line must have a reason to be there. A database listening on all addresses, instead of only the local one, is one of the most frequent and most serious mistakes.
8. Slow down login attempts
Tools such as fail2ban read the logs and temporarily block addresses that repeatedly fail to log in. With passwords disabled the risk is already low, but fail2ban is also useful for protecting other exposed services, such as an application's admin panel.
9. Think about backups and monitoring right away
Security isn't only about attacks. Before putting important data on the server, set up:
- automatic backups with at least one copy outside the provider;
- an external reachability check that warns you if the service doesn't respond;
- a disk space alert, because a full disk stops databases and logs.
10. Write everything down
Every operation performed on the server should be noted, or better still turned into a script or a configuration playbook. The next server will be ready in minutes, identical to the first, and you will always know how it was configured.
In short
- Update the system as soon as the server is created and enable automatic security updates.
- Use a personal user with
sudoand log in only with SSH keys, disabling passwords and root login. - Enable a firewall that closes everything except the necessary services, watching out for ports published by Docker.
- Check with
ss -tulpnwhat is listening and never expose databases to the Internet. - Prepare backups, monitoring and documentation before putting important data into production.