How to Harden SSH on Fedora (Complete Security Guide)

Secure Fedora SSH by disabling root login and password authentication in sshd_config.

The scenario

You just finished provisioning a fresh Fedora Server instance. You can log in with a password from anywhere on the internet. That works fine until a botnet starts scanning your IP address. Within hours, you will see dozens of failed login attempts in your logs. The default SSH configuration prioritizes convenience over security. That convenience becomes a liability the moment your server faces the public internet. You need to lock down the daemon without locking yourself out.

What SSH hardening actually does

SSH hardening is not about adding a firewall rule and calling it done. It is about reducing the attack surface of the sshd process. Think of it like changing the locks on a building. You remove the master key that everyone knows. You stop the front desk from handing out keys to strangers. You restrict access to specific employees. The daemon still runs. It still accepts connections. It just refuses to authenticate anyone who does not present the exact credentials you configured.

Fedora ships with a sensible default configuration. It does not ship with a hardened one. The configuration lives in /etc/ssh/sshd_config. Modern Fedora versions also support drop-in files in /etc/ssh/sshd_config.d/. Editing the main file works fine for a single server. Using drop-in files keeps your customizations separate from package updates. I will show the main file approach here because it is easier to audit. The principles apply to both. Remember that files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/.

The sshd daemon reads the configuration file once at startup. It does not watch for changes. You must reload or restart the service after every edit. A reload applies changes without dropping existing connections. A restart kills all active sessions. Use reload when you are testing changes on a live system.

Run sshd -t before you reload. It catches syntax errors before they break your system.

Step-by-step configuration

Before you change anything, open a second terminal window or keep your current session active. If you make a typo and restart the service, you will lose access to the first window. Keep a live connection open until you verify the new configuration works.

Here is how to check the current state of the SSH daemon and confirm it is listening on the expected port.

sudo systemctl status sshd
# Shows active state, recent journal lines, and confirms the main PID
sudo ss -tlnp | grep sshd
# Verifies the daemon is bound to port 22 on all interfaces

The main configuration file uses a simple key-value format. Lines starting with # are comments. If a setting appears twice, the first match wins. That rule catches people who copy-paste settings at the bottom of the file without realizing the original value is still active higher up. Always search for the exact key before editing.

Here is how to disable direct root login and force key-based authentication.

sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
# Replaces the commented default or existing value with an explicit deny
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
# Removes password guessing as a valid authentication method
sudo sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
# Ensures the daemon accepts SSH keys instead of passwords

Those three lines cover the basics. You should also restrict which users can log in. This prevents service accounts or compromised low-privilege accounts from escalating access through SSH.

Here is how to whitelist specific users and restrict access to a dedicated group.

echo "AllowUsers yourusername backupadmin" | sudo tee -a /etc/ssh/sshd_config
# Appends a whitelist. Only these exact usernames can authenticate
echo "AllowGroups sshusers" | sudo tee -a /etc/ssh/sshd_config
# Alternative to AllowUsers. Members of this group can log in

You need to generate an SSH key pair if you do not already have one. The ssh-keygen command creates a public and private key. The public key goes on the server. The private key stays on your workstation. Never share the private key.

Here is how to generate a modern Ed25519 key pair and deploy it to the server.

ssh-keygen -t ed25519 -C "fedora-workstation"
# Creates a cryptographically strong key pair in ~/.ssh/
ssh-copy-id -i ~/.ssh/id_ed25519.pub yourusername@server-ip
# Uploads the public key to the remote ~/.ssh/authorized_keys file

After updating the configuration, you must reload the daemon. A reload applies changes without dropping existing connections. A restart kills all active sessions. Use reload when you are testing changes on a live system.

Here is how to safely apply the new configuration and check for syntax errors.

sudo sshd -t
# Validates the config file syntax before applying changes
sudo systemctl reload sshd
# Applies the new settings without terminating active sessions

Always run firewall-cmd --reload after every rule change. Otherwise the runtime config and the persistent config diverge.

Verify the changes

Open a new terminal window on your workstation. Do not close your existing session yet. Attempt to connect using the new configuration. The daemon will reject password prompts and refuse root access.

Here is how to test the connection and confirm the daemon enforces your rules.

ssh yourusername@server-ip
# Should prompt for the SSH key passphrase, not a password
ssh root@server-ip
# Should immediately return "Permission denied (publickey)"

Check the server logs to confirm the daemon processed the connection correctly. The journalctl command shows exactly why a connection succeeded or failed. Most sysadmins type journalctl -xeu sshd muscle-memory style. The x flag adds explanatory text and the e flag jumps to the end.

Here is how to review the authentication logs and verify the daemon behavior.

sudo journalctl -xeu sshd --since "5 minutes ago"
# Shows recent SSH daemon logs with explanatory context
sudo grep "Accepted publickey" /var/log/secure
# Confirms the key-based authentication actually completed

If the connection works and the logs show public key acceptance, your hardening is active. Close the old session. You can now manage the server securely.

Reboot before you debug. Half the time the symptom is gone.

Common pitfalls and error messages

The most common mistake is locking yourself out by disabling password authentication before verifying the SSH key works. If you see Permission denied (publickey) on your first test connection, do not panic. Your original session is still open. Check ~/.ssh/authorized_keys on the server. The permissions must be strict.

Here is how to fix broken SSH key permissions and restore access.

chmod 700 ~/.ssh
# Restricts the directory to the owner only
chmod 600 ~/.ssh/authorized_keys
# Prevents other users from reading the key list
sudo systemctl restart sshd
# Forces the daemon to re-read the corrected permissions

Another frequent issue involves SELinux denials. If you move the authorized_keys file or change the home directory location, SELinux will block SSH from reading it. The daemon will silently fail to authenticate the key. You will see Permission denied (publickey) in the client output.

Here is how to restore correct SELinux contexts after moving SSH files.

sudo restorecon -Rv ~/.ssh
# Resets security contexts to the default SSH policy
sudo ausearch -m avc -ts recent
# Shows recent SELinux denials if the context is still wrong

If you edited the configuration file and the daemon refuses to start, you will see a clear syntax error in the logs. The sshd -t command catches these before they break your system. Do not ignore the output. Fix the typo before reloading.

If you see [FAILED] Failed to start sshd.service during boot, your configuration probably references a missing key file or contains a malformed directive. Run journalctl -xeu sshd immediately. Read the actual error before guessing.

Trust the package manager. Manual file edits drift, snapshots stay.

When to apply these settings

Use password authentication when you are managing a local development machine behind a firewall. Use key-based authentication when you expose the server to the public internet or a shared network. Use AllowUsers when you need to restrict access to a small, fixed list of administrators. Use AllowGroups when your team rotates frequently and you manage access through group membership. Use PermitRootLogin prohibit-password when you need root access for emergency recovery but still want to block brute-force attacks. Stay on the default configuration if you only connect from a trusted LAN and do not handle sensitive data.

Where to go next