A True Story at 2 AM
When I first set up a VPS running Fedora, I left SSH with a default password on port 22. The next morning I opened the logs and saw over 4,000 failed login attempts from different IPs — automated brute force. I wasn’t compromised thanks to a strong password, but that was the moment I decided to do things right from the start: SSH keys + hardening + firewalld, no compromises.
I’ve been using Fedora as my main development machine for 2 years now. One thing worth noting: Fedora ships package updates fast, and OpenSSH gets new versions frequently — some configuration options change or get deprecated. This article documents the workflow I’m actually using in production, updated for Fedora 40/41.
Why Is the Default SSH Configuration Dangerous?
Fedora Server ships with openssh-server pre-installed, but the out-of-the-box configuration has a few problems:
- Port 22 — bots scan the entire internet for this port 24/7
- PasswordAuthentication yes — makes brute force attacks possible
- PermitRootLogin yes (or
prohibit-password) — root can still log in - No user restrictions — any account can SSH in
firewalld on Fedora allows SSH (port 22) through the public zone by default. That means the moment the machine boots, port 22 is already exposed to the internet. Combine that with a loose configuration like this — your server becomes an immediate target.
Your Options
Option 1: Change the SSH Port
Many people simply change port 22 to something else (e.g., 2222) and call it done. In reality, an nmap port scan will find it within minutes. This is security through obscurity — it reduces log noise, but it’s not real security.
Option 2: Fail2ban
Automatically blocks IPs after N failed attempts. Better, but still keeps password auth enabled. If an attacker uses a distributed botnet with hundreds of rotating IPs, Fail2ban becomes ineffective.
Option 3: SSH Keys + Disable Password Auth + firewalld Rules
This is the approach I chose because it’s far more thorough than either of the other two. Three layers combined: key-based auth only, hardened sshd_config, and IP whitelisting via firewalld where possible. Brute force bots become completely useless — there’s no password to guess, nothing to brute force.
Hands-On: Setting Up SSH Keys on Fedora
Step 1: Check and Install OpenSSH
# Check if already installed
dnf list installed openssh-server
# If not installed
sudo dnf install -y openssh-server
# Enable and start the service
sudo systemctl enable --now sshd
# Check service status
sudo systemctl status sshd
Step 2: Generate an SSH Key Pair on the Client Machine
Run this on your local machine, not on the server:
# ED25519 — modern algorithm, more secure than RSA 2048
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/fedora_server
# If you need broader compatibility, use RSA 4096 instead
# ssh-keygen -t rsa -b 4096 -C "[email protected]" -f ~/.ssh/fedora_server
This produces 2 files: ~/.ssh/fedora_server (private key — never share this) and ~/.ssh/fedora_server.pub (public key — this goes on the server).
Step 3: Copy the Public Key to the Server
# The quickest way
ssh-copy-id -i ~/.ssh/fedora_server.pub username@server_ip
# Or manually if ssh-copy-id isn't available
cat ~/.ssh/fedora_server.pub | ssh username@server_ip \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Step 4: Test the Connection Before Hardening
# Test with the new key — do NOT close your current session
ssh -i ~/.ssh/fedora_server username@server_ip
# Add to ~/.ssh/config for convenience
Host fedora-server
HostName server_ip
User username
IdentityFile ~/.ssh/fedora_server
IdentitiesOnly yes
Important: keep your existing SSH session open while doing the hardening steps. Locking yourself out is entirely possible — the old session is your escape route.
Hardening sshd_config
The main configuration file is /etc/ssh/sshd_config. On Fedora 40+, the recommended approach is to use /etc/ssh/sshd_config.d/*.conf for overrides — you leave the original file untouched, making rollbacks much easier.
# Create a separate override file
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
File contents:
# Disable password authentication
PasswordAuthentication no
KbdInteractiveAuthentication no
# Disallow direct root login
PermitRootLogin no
# Restrict SSH access to specific users (replace "youruser" with your actual username)
AllowUsers youruser
# Limit the number of authentication attempts
MaxAuthTries 3
# Idle session timeout (300 seconds = 5 minutes)
ClientAliveInterval 300
ClientAliveCountMax 2
# Disable X11 forwarding if not needed
X11Forwarding no
# Disable unnecessary auth methods
GSSAPIAuthentication no
UsePAM yes
Apply the configuration:
# Check syntax before restarting
sudo sshd -t
# If no errors
sudo systemctl restart sshd
firewalld Integration
Unlike Ubuntu which uses ufw, Fedora uses firewalld with the concepts of zones and services. It’s more flexible, but you need to understand how it works before making changes.
Check the Current Configuration
# View active zones
sudo firewall-cmd --get-active-zones
# View services currently allowed in the public zone
sudo firewall-cmd --zone=public --list-services
Change the SSH Port and Update firewalld
If you decide to change the port (e.g., 2222) to reduce log noise:
# Append to the hardening file
echo "Port 2222" | sudo tee -a /etc/ssh/sshd_config.d/99-hardening.conf
# Tell SELinux about the new port (Fedora uses SELinux enforcing by default)
sudo semanage port -a -t ssh_port_t -p tcp 2222
# Update firewalld: remove the default ssh service (port 22), add the new port
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
# Restart sshd
sudo systemctl restart sshd
Whitelist IPs if You Have a Static IP
If you work from a static IP — a fixed office network or VPN — this is the strongest layer of protection you can add:
# Create a dedicated zone for SSH management
sudo firewall-cmd --permanent --new-zone=ssh-management
sudo firewall-cmd --permanent --zone=ssh-management --add-port=2222/tcp
# Allow only your IP
sudo firewall-cmd --permanent --zone=ssh-management --add-source=YOUR.OFFICE.IP/32
# Make sure the public zone does NOT have SSH
sudo firewall-cmd --permanent --zone=public --remove-service=ssh
sudo firewall-cmd --permanent --zone=public --remove-port=2222/tcp
sudo firewall-cmd --reload
# Verify
sudo firewall-cmd --zone=ssh-management --list-all
Rich Rules for Rate Limiting
Don’t want to install Fail2ban? firewalld can handle rate limiting on its own:
# Limit to 5 new SSH connections per minute from a single IP
sudo firewall-cmd --permanent --add-rich-rule=\
'rule service name="ssh" limit value="5/m" accept'
sudo firewall-cmd --reload
Verification and Monitoring
# Check the active SSH configuration
sudo sshd -T | grep -E 'passwordauth|permitroot|port|allowusers'
# Watch live login logs
sudo journalctl -u sshd -f
# Filter failures from the past hour
sudo journalctl -u sshd --since "1 hour ago" | grep -i fail
Final Checklist
Before closing the old session, I run through this list:
- SSH with the new key works from a separate terminal ✓
PasswordAuthentication nois in effect (try SSH with a password — it must be rejected) ✓- Root login is blocked ✓
firewall-cmd --list-allshows the correct port/zone ✓- SELinux is not reporting AVC denials for the new port (check:
sudo ausearch -m avc -ts recent) ✓
The whole process takes about 15–20 minutes. The first time might take 45 minutes because SELinux tends to produce unfamiliar error messages that need careful reading. But once you’re used to it, setting up a new Fedora server is much faster — sshd_config.d/ drop-ins keep things clean, firewalld zones are clearly defined, and there’s far less guesswork compared to other distros I’ve worked with.
