Server access and hardening

Use personal accounts and sudo

Log in as an attributable unprivileged user and elevate only the commands that need it.

A fresh VPS usually gives you one thing: a root login. The temptation is to give that key to everyone who needs the server. It works, and it destroys two things at once. You can no longer tell who did what, and one stolen laptop means rotating access for the whole team.

The fix is boring and it is what every well-run server does. One account per person, no shared root, and sudo for the few commands that need privilege.

Create a personal account

On the server, as the initial admin:

sudo adduser anna --disabled-password --gecos 'Anna Rossi'
sudo mkdir -p /home/anna/.ssh
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL3k... anna laptop' | sudo tee /home/anna/.ssh/authorized_keys
sudo chmod 700 /home/anna/.ssh
sudo chmod 600 /home/anna/.ssh/authorized_keys
sudo chown -R anna:anna /home/anna/.ssh

--disabled-password means Anna can only get in with her key. The chown line matters: sshd refuses an authorized_keys owned by someone other than the account.

Anna tests from her laptop, in a new terminal, while yours stays open:

ssh [email protected] 'id'
uid=1001(anna) gid=1001(anna) groups=1001(anna)

No sudo group yet. She can look around and nothing more.

Grant the minimum

Adding Anna to the sudo group gives her everything. Sometimes that is right. Often a person needs one or two privileged commands. Give exactly those:

sudo visudo -f /etc/sudoers.d/anna
anna ALL=(root) NOPASSWD: /usr/bin/systemctl restart notes-api, /usr/bin/journalctl -u notes-api *

visudo checks the syntax before saving, so a typo cannot lock everyone out of sudo. Anna can now confirm what she is allowed:

sudo -l
User anna may run the following commands on notes:
    (root) NOPASSWD: /usr/bin/systemctl restart notes-api, /usr/bin/journalctl -u notes-api *

Try sudo systemctl restart notes-api and it works. Try sudo apt update and you get Sorry, user anna is not allowed to execute '/usr/bin/apt update' as root on notes. That refusal is the boundary you just drew.

Everything is attributed

Now look at what the server recorded:

sudo journalctl _COMM=sudo --since '10 minutes ago'
sudo[4192]: anna : TTY=pts/1 ; PWD=/home/anna ; USER=root ; COMMAND=/usr/bin/systemctl restart notes-api
sudo[4207]: anna : command not allowed ; TTY=pts/1 ; PWD=/home/anna ; USER=root ; COMMAND=/usr/bin/apt update

Both the allowed restart and the refused apt carry Anna’s name. With a shared root key, every line would say root and you would learn nothing.

Revocation is per person

When Anna leaves the project, you remove her, and nobody else notices:

sudo usermod --lock --expiredate 1 anna
sudo rm /etc/sudoers.d/anna

--lock alone only disables the password, and a key login does not use the password. --expiredate 1 expires the whole account, so sshd refuses Anna’s key too. Her next attempt gets Your account has expired and the connection closes. Compare that with a shared root key, where offboarding one person means generating a new key and redistributing it to everyone.

Create a second account, marco, with a different sudoers.d file, and check that locking one does not affect the other. Two people, two keys, two log trails, two independent switches.

Lesson completed