SSH Security and Key-Based Access

Secure remote access on Debian using SSH keys instead of passwords, with proper hardening, validation, and real-world defensive practices.

What this is

This guide walks through securing SSH on Debian using key-based authentication instead of passwords, then tightening access to reduce real attack surface.

Why this matters

• SSH is one of the most scanned services on the internet

• Password logins are constantly targeted by brute-force attacks

• Key-based authentication removes guessable login secrets from the server-side authentication path

• A secure SSH setup becomes the foundation for safely managing everything else

Important

Do NOT lock yourself out

Always test key login in a separate session before disabling passwords.

On Debian, SSH behavior can also be affected by files in /etc/ssh/sshd_config.d/, so validation before restart matters. Debian’s OpenSSH configuration includes drop-ins from that directory, and those settings can override the main config.

What you need

• Debian system

• sudo access

• second machine to connect from

• a user account intended for administration

Build

Step 1: Update the system

sudo apt update
sudo apt full-upgrade -y

What this does:
Refreshes package metadata and installs current upgrades.

Why this matters:
Remote access should not sit on outdated packages if avoidable.

What to expect:
Debian updates package lists, then installs available upgrades.

Step 2: Install OpenSSH server

sudo apt install -y openssh-server

What this does:
Installs the SSH server package so Debian can accept remote SSH connections.

Why this matters:
The SSH client alone is not enough for inbound administration.

What to expect:
If it is already present, Debian will report that nothing new is needed.

Step 3: Enable and start SSH

sudo systemctl enable ssh
sudo systemctl start ssh

What this does:
Ensures SSH starts now and automatically after reboot.

Why this matters:
A reboot should not silently remove your remote access path.

What to expect:
Little or no output is normal.

Step 4: Verify SSH is running and listening

sudo systemctl status ssh
sudo ss -tulpn | grep ssh

What this does:
Checks service health and confirms a listening SSH socket.

Why this matters:
You want a known-good baseline before changing authentication behavior.

What to expect:
The service should show as active, and SSH should appear as listening, normally on port 22.

Step 5: Generate a key pair on the client machine

ssh-keygen -t ed25519 -a 100

What this does:
Creates an Ed25519 SSH key pair on the client machine. OpenSSH supports Ed25519, and the -a option increases KDF rounds for the private key file.

Why this matters:
Key-based authentication is far stronger than exposing password logins to repeated guessing attempts.

What to expect:
You will choose a file location and optionally a passphrase. A passphrase is recommended.

Step 6: Copy the public key to the server

ssh-copy-id youruser@YOUR-SERVER-IP

What this does:
Installs your public key into the target user’s ~/.ssh/authorized_keys.

Why this matters:
This is what allows the server to recognize your private key during login.

What to expect:
This step commonly uses the existing password login one last time. That is normal.

If ssh-copy-id is missing:

cat ~/.ssh/id_ed25519.pub | ssh youruser@YOUR-SERVER-IP "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Why this matters:
The fallback still works, but it must preserve sane SSH directory and file permissions.

Step 7: Test key-based login before changing server authentication

ssh youruser@YOUR-SERVER-IP

What this does:
Attempts a fresh SSH login using your key.

Why this matters:
Never disable password authentication until this succeeds from a separate session.

What to expect:
You should reach the shell without needing the account password. If your private key has a passphrase, you may be asked for that instead.

Step 8: Back up the current SSH configuration

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

What this does:
Saves a copy of the main SSH config file before hardening changes.

Why this matters:
Rollback is much easier when you preserve the known-good baseline first.

Step 9: Create a Debian SSH hardening drop-in

sudo nano /etc/ssh/sshd_config.d/10-hardening.conf

Add:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
UsePAM yes
MaxAuthTries 3
X11Forwarding no

Optional: If only one or a few known admin accounts should ever log in over SSH, you can also add:

AllowUsers youruser

What this does:
Creates a dedicated hardening override using Debian’s SSH drop-in mechanism. Debian’s OpenSSH configuration includes /etc/ssh/sshd_config.d/*.conf, and drop-ins can override values from the main file.

Why this matters:
This is cleaner than stuffing every change into the main config, and it makes your hardening easier to review later.

Why disable both password directives:
With UsePAM yes, PAM-backed keyboard-interactive auth can still provide a password-style login path, so disabling both PasswordAuthentication and KbdInteractiveAuthentication is the cleanest way to enforce keys-only access.

Why AllowUsers is optional:
It can be a strong restriction, but only use it if you are certain which accounts should be allowed. Otherwise, it can block legitimate admin access later.

What to expect:
The changes do not take effect until you validate and restart SSH.

Step 10: Validate the configuration before restart

sudo sshd -t

What this does:
Checks SSH configuration syntax before the service is restarted.

Why this matters:
A syntax error can break remote access after restart.

What to expect:
No output usually means the configuration is valid.

Step 11: Review the effective authentication settings

sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|usepam|maxauthtries|x11forwarding'

What this does:
Shows the effective values SSH plans to use.

Why this matters:
This helps catch confusion from inherited defaults or drop-in overrides before restart.

What to expect:
You should see the settings reflected exactly as intended.

Step 12: Restart SSH

sudo systemctl restart ssh

What this does:
Reloads SSH with the new hardening settings.

Why this matters:
Configuration changes do nothing until SSH is restarted or reloaded.

Step 13: Re-test login in a fresh session

ssh youruser@YOUR-SERVER-IP

What this does:
Confirms that the final, hardened setup still works.

Why this matters:
A secure remote access configuration is only useful if legitimate administration still succeeds.

Hardening

Important SSH security improvements

Fix ownership and permissions on server-side SSH files

sudo chown -R youruser:youruser /home/youruser/.ssh
sudo chmod 700 /home/youruser/.ssh
sudo chmod 600 /home/youruser/.ssh/authorized_keys

What this does:
Ensures the server-side SSH directory and authorization file are owned by the correct user and not too permissive.

Why this matters:
Incorrect ownership or loose permissions are a common reason key-based login fails in practice.

Verify the UFW OpenSSH profile before relying on it

sudo apt install -y ufw
sudo ufw app list
sudo ufw app info OpenSSH

What this does:
Confirms the OpenSSH application profile exists before using it in firewall rules. UFW supports application profiles like OpenSSH.

Why this matters:
It is safer to verify than assume, especially on a remote system.

Preferred: restrict SSH to a trusted source IP

sudo ufw allow from YOUR-IP to any port 22 proto tcp
sudo ufw enable
sudo ufw status

What this does:
Limits SSH access to a specific source IP and then enables the firewall.

Why this matters:
Reducing who can even reach SSH is one of the strongest practical improvements for small environments.

If you cannot restrict by source IP, allow the OpenSSH profile first

sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status

What this does:
Allows SSH traffic, enables UFW, and verifies the resulting policy.

Why this matters:
Enabling a firewall before allowing your access path is a classic self-lockout mistake.

Keep SSH internal or behind a VPN if possible

If SSH does not need broad internet exposure, keep it on an internal network or behind a VPN path.

Why this matters:
A non-public administrative surface gets scanned and attacked far less.

Protect the private key

Do not email it, sync it casually, or leave it unencrypted on untrusted devices.

Why this matters:
Key-based access is only strong if the private key stays private.

Common Mistakes

What commonly goes wrong

• disabling password login before a successful key-based test

• forgetting that sshd_config.d drop-ins can override the main config

• wrong ownership or permissions on server-side ~/.ssh or authorized_keys

• exposing SSH broadly when it could have been restricted by IP or VPN

• checking only that SSH is running, but not whether the intended authentication settings are actually active

Testing

Verify that the setup really works

sudo systemctl status ssh
sudo sshd -T | grep -E 'passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|permitrootlogin'
journalctl -u ssh --since "15 minutes ago"
sudo tail -f /var/log/auth.log

What to confirm:

• key login succeeds

• password login fails if you disabled it

• direct root login is blocked

• SSH survives reboot and still starts automatically

Monitoring

What to watch after setup

Watch for:

• repeated failed login attempts

• attempts against usernames you do not use

• unknown source IPs

• sudden spikes in authentication failures

SSH activity is especially useful to monitor in auth logs, firewall logs, and later in central logging or SIEM workflows.

Low-Cost Upgrade

Make the setup stronger

• Add Fail2Ban for repeated failed login attempts

• Pair SSH with a restrictive UFW policy

• Put remote administration behind a VPN

• Centralize auth logs

What comes next

• UFW configuration and firewall strategy

• Fail2Ban and brute-force protection

• Secure remote administration workflows

• Log centralization and analysis