Hardening SSH on Ubuntu: Custom Admin User and Locking Down Access
Your shiny new Ubuntu server is online. Congratulations — and so are the bots. Within minutes of going live, automated scanners are already hammering port 22, trying root, admin, ubuntu, and every password in the dictionary. The default setup is a welcome mat for attackers.
This guide walks you through hardening SSH from scratch: creating a dedicated admin user, moving SSH off the default port, enforcing key-only authentication, and locking everything down with a firewall. Every command is copy-paste-ready and uses variables you set once at the top.
Let's make your server boring to attackers.
Inputs: Set Your Variables Once
Enter your server information below. Every code block and command in this article will update instantly to reflect these values, making them ready to copy and paste directly into your terminal.
Enter your values below — every code block in this article updates instantly.
1. Threat Model: Why Defaults Are Dangerous
Every Ubuntu cloud instance starts the same way:
- SSH on port 22 — the first port every scanner checks
- A predictable user (
ubuntu,root,admin) — half the login is already guessed - Often password authentication enabled — brute-force paradise
Automated tools like Hydra and Medusa run 24/7, cycling through millions of credential combinations. If you leave defaults in place, you're not asking if you'll be compromised — you're asking when.
Here's what we're going to do about it:
| Default | After Hardening |
|---|---|
| Port 22 | Custom port ($SSH_PORT) |
User: ubuntu / root | Custom user: $ADMIN_USER |
| Password auth enabled | Key-only authentication |
| Root login allowed | Root login blocked |
| No firewall | UFW with explicit allow rules |
2. Create a New Admin User
First, SSH into your server with the default user:
ssh $DEFAULT_USER@$SERVER_IP
Create the user and grant sudo
# Create the new user (you'll be prompted for a password)
sudo adduser $ADMIN_USER
# Add to the sudo group
sudo usermod -aG sudo $ADMIN_USER
Copy your SSH keys from the default user
The default user already has your public key in ~/.ssh/authorized_keys. Let's copy it to the new user:
# Create .ssh directory for the new user
sudo mkdir -p /home/$ADMIN_USER/.ssh
# Copy the authorized_keys file
sudo cp /home/$DEFAULT_USER/.ssh/authorized_keys /home/$ADMIN_USER/.ssh/authorized_keys
# Set correct ownership and permissions
sudo chown -R $ADMIN_USER:$ADMIN_USER /home/$ADMIN_USER/.ssh
sudo chmod 700 /home/$ADMIN_USER/.ssh
sudo chmod 600 /home/$ADMIN_USER/.ssh/authorized_keys
Why these permissions matter: SSH is extremely strict about file permissions. If
.sshisn't700orauthorized_keysisn't600, SSH will silently refuse key authentication and you'll be locked out.
Verify sudo access
Before proceeding, confirm the new user works:
su - $ADMIN_USER
sudo whoami
# Should output: root
If you see root, you're good. Exit back to the default user with exit.
3. Change the SSH Port
Moving SSH off port 22 eliminates the vast majority of automated attacks. It won't stop a targeted attacker, but it dramatically reduces noise.
sudo sed -i "s/^#Port 22/Port $SSH_PORT/" /etc/ssh/sshd_config
Or if you prefer editing manually:
sudo nano /etc/ssh/sshd_config
Find the line #Port 22 and change it to:
Port $SSH_PORT
(Replace $SSH_PORT with your chosen value.)
⚠️ CRITICAL: Do NOT restart SSH yet. If you restart now without opening the new port in your firewall, you will be permanently locked out of your server. We'll configure the firewall first, then restart.
Cloud users (AWS, DigitalOcean, GCP): You must ALSO update your cloud provider's Security Group or firewall rules to allow inbound traffic on
$SSH_PORT. The OS-level firewall (UFW) alone is not enough if your cloud network blocks the port.
4. Harden SSH Settings
While we have sshd_config open, let's lock things down properly. Add or modify these directives:
sudo nano /etc/ssh/sshd_config
# Prevent root from logging in via SSH
PermitRootLogin no
# Disable password-based authentication (keys only)
PasswordAuthentication no
# Limit login attempts per connection to 3
MaxAuthTries 3
# Allow only 2 simultaneous sessions per connection
MaxSessions 2
# Disable TCP keepalive (prevents certain spoofing attacks)
TCPKeepAlive no
# Disconnect idle sessions after 50 minutes (3000 seconds)
ClientAliveInterval 3000
# No second chance — disconnect immediately after the interval
ClientAliveCountMax 0
# Only allow your admin user to connect via SSH
AllowUsers $ADMIN_USER
(The AllowUsers line above will automatically reflect your $ADMIN_USER.)
What each setting does
| Directive | Effect |
|---|---|
PermitRootLogin no | Blocks all SSH access for the root account, even with a valid key |
PasswordAuthentication no | Forces key-based authentication — no passwords accepted |
MaxAuthTries 3 | Disconnects after 3 failed authentication attempts per session |
MaxSessions 2 | Limits multiplexed sessions per connection to 2 |
TCPKeepAlive no | Disables TCP-level keepalive packets to prevent spoofed connections |
ClientAliveInterval 3000 | Server sends a check every 3000 seconds (50 min) to see if the client is alive |
ClientAliveCountMax 0 | Terminates the session immediately if the client doesn't respond to the alive check |
AllowUsers $ADMIN_USER | Whitelist — only this user can connect. Everyone else is denied before authentication even starts |
Tip:
AllowUsersis one of the most powerful SSH hardening directives. Even if an attacker knows a valid username+key on your system, if that user isn't in theAllowUserslist, SSH rejects them at the door.
5. Install and Configure Firewall (UFW)
UFW (Uncomplicated Firewall) is Ubuntu's friendly frontend for iptables. Let's make sure only the traffic we want gets through.
# Update packages and install UFW (usually pre-installed on Ubuntu)
sudo apt update && sudo apt install ufw -y
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow your custom SSH port
sudo ufw allow $SSH_PORT/tcp
# Allow HTTP and HTTPS for web traffic
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# If port 22 was previously allowed, remove it
sudo ufw delete allow 22/tcp
# Enable the firewall
sudo ufw enable
# Verify the rules
sudo ufw status verbose
You should see output similar to:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
$SSH_PORT/tcp ALLOW IN Anywhere
80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
Optional: Restrict SSH by Source IP
If you always connect from a known IP (e.g., your office), you can restrict SSH access to that IP only:
# Allow SSH only from your specific IP
sudo ufw allow from 203.0.113.50 to any port $SSH_PORT proto tcp
# Then remove the general SSH rule
sudo ufw delete allow $SSH_PORT/tcp
This is the tightest lockdown possible — even if someone has your key, they can't connect from a different network.
6. Restart and Test
This is the most critical step. Follow this safety checklist exactly.
Step 1: Validate the SSH config
sudo sshd -t
If this outputs nothing, your config is valid. If it shows errors, fix them before proceeding.
Step 2: Restart SSH
sudo systemctl restart ssh
On some Ubuntu versions, the service is called
sshdinstead ofssh. If the above fails, trysudo systemctl restart sshd.
Step 3: Test from a NEW terminal
⚠️ DO NOT CLOSE YOUR EXISTING SSH SESSION. Open a completely separate terminal window and test:
ssh -p $SSH_PORT $ADMIN_USER@$SERVER_IP
Only close the original session after you've confirmed:
- ✅ You can connect on the new port
- ✅ You can run
sudocommands - ✅ The new user is working correctly
If the new connection fails, you still have your original session open to fix the issue. If you close it prematurely, and something is wrong, you're locked out.
7. Retire the Default User
Once you've verified everything works with $ADMIN_USER, it's time to deal with the default ubuntu user.
Option A: Delete the user entirely
sudo deluser --remove-home $DEFAULT_USER
This removes the user account and their home directory. Use this when you're confident you no longer need anything in their home folder.
Option B: Lock the user (safer)
sudo usermod --lock $DEFAULT_USER
This disables password login for the user but preserves their home directory and files. The account still exists but can't authenticate.
When to choose which:
| Scenario | Recommendation |
|---|---|
Fresh server, nothing important in /home/ubuntu | Delete — reduces attack surface |
| Production server, scripts reference the default user | Lock — preserves files, prevents login |
| Not sure | Lock first, delete later after auditing |
8. Final State Summary
After completing all steps, your server is now protected by multiple layers:
- ✅ SSH moved off port 22 — automated scanners skip right past you
- ✅ Key-only authentication — no passwords to brute-force
- ✅ Root login blocked — even if root has a key, SSH refuses the connection
- ✅ Only
$ADMIN_USERallowed —AllowUserswhitelist rejects all other usernames - ✅ Firewall active — only ports
$SSH_PORT,80, and443accept incoming traffic - ✅ Default user retired — no more predictable
ubuntuaccount - ✅ Idle sessions terminated — ghost connections are cleaned up automatically
Common Pitfalls
These are the mistakes that lock people out of their own servers. Learn from others' pain:
1. Forgetting to open the new port before restarting SSH
If you change the port to 2222 and restart SSH without adding a UFW rule for 2222, you're locked out immediately. Always configure the firewall first.
2. Wrong permissions on .ssh
SSH fails silently if .ssh isn't 700 or authorized_keys isn't 600. You'll see "Permission denied (publickey)" with no further explanation. Double-check with:
ls -la /home/$ADMIN_USER/.ssh/
3. Restarting SSH before testing the config
Always run sudo sshd -t first. A typo in sshd_config will prevent SSH from starting entirely.
4. Cloud Security Group still blocks the port
UFW is the OS-level firewall. On AWS, GCP, Azure, and DigitalOcean, there's a second network-level firewall (Security Groups, VPC firewall rules, etc.). Both must allow your new port. If UFW allows it but the cloud firewall blocks it, you can't connect.
5. Closing the original session too early
Never close your existing SSH session until you've verified the new connection works in a separate terminal. This is your lifeline.
6. Typo in AllowUsers
If you mistype the username in AllowUsers, nobody can log in via SSH. Triple-check the spelling matches your $ADMIN_USER exactly.
That's it. Your server just went from "hello, internet" to "talk to the firewall." Every automated scanner and script kiddie is now hitting a wall. The only way in is with the right key, the right user, on the right port — and the firewall has to agree.
Stay safe. Stay patched. And never, ever close that first terminal.
