The VPS answers on port 22 to the whole internet, as debian, with password-less sudo. This page moves you to your own account, puts SSH on its own port with keys only, turns on a firewall, bans brute force and makes security updates install themselves. The order matters: every new door is tested from a second terminal before the old one closes.
Before you start
Check that the delivered login works and that debian has sudo without a password, then log in as debian in a first terminal and stay logged in: a second terminal on your laptop tests each change from the outside.
Two terminals, and the badge on each block says which one:
- Terminal A, badge server · debian: your first session, opened now. It is your lifeline: do not close it until this page tells you to. Every
sudocommand up to the switch-over runs here. - Terminal B, badge Mac: a terminal on your laptop, for the tests from outside.
takes over only once the new door is tested (a third session, ssh vps). The server key is the one made on page 2, ~/.ssh/id_ed25519_vps.
$ssh -i ~/.ssh/id_ed25519_vps -o IdentitiesOnly=yes debian@ sudo -n true && echo OKOK
Update everything
Upgrade, then reboot if Debian left the flag file that says an update needs it (a new kernel, usually), and log in again as debian:
$sudo apt update && sudo apt full-upgrade -y$sudo apt install -y vim$[ -f /var/run/reboot-required ] && sudo rebootName and clock
Give the server its name, keep cloud-init from putting OVH's name back, and set the time zone. timedatectl must then show your zone and System clock synchronized: yes.
$sudo hostnamectl set-hostname $printf 'preserve_hostname: true\nmanage_etc_hosts: false\n' | sudo tee /etc/cloud/cloud.cfg.d/99-hostname.cfg$sudo sed -i '/^127\.0\.1\.1[[:space:]]/d' /etc/hosts$echo "127.0.1.1 " | sudo tee -a /etc/hosts$sudo timedatectl set-timezone $timedatectlYour own user
Create , in terminal A. adduser asks for the password twice: type the one saved in your password manager. Paste this command alone:
$sudo adduser --gecos "" Then put it in sudo and sshusers, and write your public key (the value from the panel, not a copy of debian's file) into its authorized_keys:
$sudo groupadd -f sshusers$sudo usermod -aG sudo,sshusers $sudo install -d -m 700 -o -g /home//.ssh$echo '' | sudo tee /home//.ssh/authorized_keys >/dev/null$sudo chown : /home//.ssh/authorized_keys$sudo chmod 600 /home//.ssh/authorized_keysCheck the key before going on: the file must hold a valid Ed25519 key.
$sudo ssh-keygen -lf /home//.ssh/authorized_keys | grep -o '(ED25519)'(ED25519)
From terminal B (Mac), log in as the new user with the key only (still on port 22) and read its groups. The options forbid the password: if this passes, the key works.
$ssh -i ~/.ssh/id_ed25519_vps -o IdentitiesOnly=yes -o PasswordAuthentication=no -o BatchMode=yes @ 'id -nG | grep -qw sudo && id -nG | grep -qw sshusers && echo groups OK'groups OK
Harden SSH
Write the drop-in: copy the command and paste it in terminal A.
Port PermitRootLogin noPasswordAuthentication noKbdInteractiveAuthentication noAllowGroups sshusersTest the syntax, then read the effective values. sshd -t prints nothing when the syntax is fine; the second command must print exactly one port line with , no for the three next settings, and allowgroups sshusers.
$sudo sshd -t$sudo sshd -T | grep -Ei '^(port|permitrootlogin|passwordauthentication|kbdinteractiveauthentication|allowgroups) 'Firewall and switch-over
Install ufw, refuse everything inbound, and open the new port and 22 for now. ufw enable warns that it may disrupt SSH connections; answer y. 22 stays open so the session you are typing in survives, and so a rollback (delete the drop-in, restart) brings you back to a port the firewall accepts.
$sudo apt install -y ufw$sudo ufw default deny incoming && sudo ufw default allow outgoing$sudo ufw allow /tcp && sudo ufw allow 22/tcp$sudo ufw enableBefore the restart, give your laptop the vps shortcut, with the server key, in terminal B. If ~/.ssh/config already has a Host vps block, do not add a second one: edit it with ${EDITOR} ~/.ssh/config so it has the same lines (ssh uses the first match).
$touch ~/.ssh/config && chmod 600 ~/.ssh/config$grep -q '^Host vps$' ~/.ssh/config || printf '\nHost vps\n HostName \n Port \n User \n IdentityFile ~/.ssh/id_ed25519_vps\n IdentitiesOnly yes\n AddKeysToAgent yes\n UseKeychain yes\n' >> ~/.ssh/config$sudo systemctl restart sshIn terminal B, test the new door with the shortcut. ssh asks again to confirm the host key: it is the same key, stored under a new name ([IP]:port), so answer yes.
$ssh -o BatchMode=yes vps echo connectedconnected
If the new connection fails
Your first terminal is still logged in: nothing is lost. Look, in this order:
sudo sshd -T | grep '^port ': must say.sudo ss -tlnp | grep sshd: sshd must listen on, for IPv4 (0.0.0.0) and IPv6 ([::]).sudo ufw status:/tcp must beALLOW.systemctl is-active ssh.socket: if it saysactive, the socket unit holds the port, not sshd; runsudo systemctl daemon-reload && sudo systemctl restart ssh.socket. Debian's default isssh.service(Ubuntu uses the socket): confirm withsystemctl is-enabled ssh.socket ssh.service.- OVH's Network Firewall (control panel → your IP → Network Firewall), if you enabled it: it filters before the packets reach the VPS; add a rule for
. - To roll back in ten seconds:
sudo rm /etc/ssh/sshd_config.d/00-hardening.conf && sudo systemctl restart ssh. You are back on port 22, which is still open. - First session gone too: log in on the KVM console as
with your password and run the same commands.
The new port works: close 22, in terminal A.
$sudo ufw delete allow 22/tcpRetire the debian account
Open your own session from the Mac and check that sudo accepts your password:
$ssh vps$sudo true && echo SUDO OKOnce SUDO OK came back, close terminal A (exit). Then, in the ssh vps session. userdel prints mail spool (/var/mail/debian) not found: that is normal.
$sudo pkill -KILL -u debian$sudo userdel -r debian$sudo rm -f /etc/sudoers.d/90-cloud-init-usersFrom your laptop, check that debian is refused; the answer must end with Permission denied (publickey).
$ssh -p -o BatchMode=yes debian@ trueBan brute force
With passwords refused, guessing is useless, but bots still knock. fail2ban reads the SSH log and bans an address after three failures.
$sudo apt install -y fail2ban python3-systemdThen the jail, in one file:
[DEFAULT]bantime.increment = true[sshd]enabled = trueport = backend = systemdmaxretry = 3findtime = 10mbantime = 1h$sudo systemctl enable fail2ban && sudo systemctl restart fail2ban$sudo fail2ban-client status sshd | head -1Status for the jail: sshd
If you ban yourself
Three typos from your own address and you are out for an hour. To never ban a fixed home address, add ignoreip = 127.0.0.1/8 ::1 followed by your address () under [DEFAULT] with sudo ${EDITOR} /etc/fail2ban/jail.d/sshd.local, and restart fail2ban. To unban, from another connection (phone hotspot) or the KVM console:
$sudo fail2ban-client set sshd unbanip Automatic security updates
$sudo apt install -y unattended-upgrades apt-listchanges$printf 'APT::Periodic::Update-Package-Lists "1";\nAPT::Periodic::Unattended-Upgrade "1";\n' | sudo tee /etc/apt/apt.conf.d/20auto-upgradesAllow a reboot at when an update needs one:
Unattended-Upgrade::Automatic-Reboot "true";Unattended-Upgrade::Automatic-Reboot-Time "";$apt-config shell UU APT::Periodic::Unattended-UpgradeUU='1'
Check IPv6
The next page publishes the IPv6 address in DNS; a dead one sends IPv6 visitors into timeouts. On the server, the global address must be and must reach the internet:
$ip -6 addr show scope global$ping -6 -c 3 2001:4860:4860::8888Note the result: if ping -6 answers, page 4 publishes the IPv6 address (AAAA record); if not, it publishes none.
Snapshot the clean state (optional, paid)
In the OVH control panel, open your VPS: on its home tab, the Snapshot option offers to order it; once active, take a snapshot from the same place. Without the option, you still have the included automated backup (one day of retention). Menu labels move: OVH's guide "Using snapshots on a VPS" on docs.ovhcloud.com has the current path.
Done
| What | Before | After this page |
|---|---|---|
| Who logs in | debian, sudo without password | , key only, sudo with password |
| SSH port | 22 | |
| Root, passwords | root off, passwords possibly on (cloud-init) | both refused by your own drop-in |
| Inbound traffic | everything | /tcp only |
| Brute force | unlimited tries | 3 tries, then banned |
| Security updates | by hand | daily, automatic, reboot at |
From now on, you connect with ssh vps. Next page: Point the domain at the server (Infomaniak DNS). It publishes and in the Infomaniak DNS zone of , so the name reaches this machine before Caddy asks for a certificate.