Hardening SSH on Ubuntu: Custom Admin User and Locking Down Access

DevOps

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.

Interactive Variables

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:

DefaultAfter Hardening
Port 22Custom port ($SSH_PORT)
User: ubuntu / rootCustom user: $ADMIN_USER
Password auth enabledKey-only authentication
Root login allowedRoot login blocked
No firewallUFW 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 .ssh isn't 700 or authorized_keys isn't 600, 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

DirectiveEffect
PermitRootLogin noBlocks all SSH access for the root account, even with a valid key
PasswordAuthentication noForces key-based authentication — no passwords accepted
MaxAuthTries 3Disconnects after 3 failed authentication attempts per session
MaxSessions 2Limits multiplexed sessions per connection to 2
TCPKeepAlive noDisables TCP-level keepalive packets to prevent spoofed connections
ClientAliveInterval 3000Server sends a check every 3000 seconds (50 min) to see if the client is alive
ClientAliveCountMax 0Terminates the session immediately if the client doesn't respond to the alive check
AllowUsers $ADMIN_USERWhitelist — only this user can connect. Everyone else is denied before authentication even starts

Tip: AllowUsers is 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 the AllowUsers list, 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 sshd instead of ssh. If the above fails, try sudo 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 sudo commands
  • ✅ 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:

ScenarioRecommendation
Fresh server, nothing important in /home/ubuntuDelete — reduces attack surface
Production server, scripts reference the default userLock — preserves files, prevents login
Not sureLock 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_USER allowedAllowUsers whitelist rejects all other usernames
  • Firewall active — only ports $SSH_PORT, 80, and 443 accept incoming traffic
  • Default user retired — no more predictable ubuntu account
  • 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.


References