Fedora Workstation ships the OpenSSH server installed but switched off. The moment you switch it on, the default firewall zone already lets port 22 in, and password logins are allowed. On a laptop that joins hotel and café Wi-Fi, that is a password prompt open to everyone on the network.
This guide covers how to enable SSH on Fedora Workstation and lock it down in the same sitting. You will start sshd from GNOME Settings or systemctl, check what firewalld lets in, verify the host key on the first connection, switch to key authentication, and turn off password and root logins with a drop-in file. Each step was run against a second Fedora machine acting as the client, and the errors in the last section are the ones we hit along the way.
Run on Fedora 45 Workstation in October 2026, SELinux enforcing, firewalld on the default zone.
What each Fedora edition does with SSH by default
Workstation is the odd one out. Server and Cloud start sshd at boot, because they expect to be managed remotely. Workstation leaves it disabled through its systemd preset:
| Edition | sshd at boot | Firewall and SSH |
|---|---|---|
| Workstation | disabled (preset) | FedoraWorkstation zone allows SSH |
| Server | enabled | FedoraServer zone allows ssh, cockpit, dhcpv6-client |
| Cloud | enabled | no firewalld installed |
On Cloud images, cloud-init also writes PasswordAuthentication no into /etc/ssh/sshd_config.d/50-cloud-init.conf. Workstation has no such file. Its effective settings show what you get the moment you start the service:
sudo sshd -T | grep -iE "^(passwordauthentication|permitrootlogin|pubkeyauthentication|kbdinteractiveauthentication|port|maxauthtries) "
Passwords are on, and root may log in with a key:
Port 22
MaxAuthTries 6
PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication yes
KbdInteractiveAuthentication no
The KexAlgorithms line in the full output opens with mlkem768x25519-sha256, a post-quantum hybrid. You don’t have to configure it. Clients from the same OpenSSH generation negotiate it automatically, as Step 4 shows.
Step 1: Check whether SSH is running
On a fresh Workstation install, both checks come back negative:
systemctl is-enabled sshd; systemctl is-active sshd
The service is disabled and stopped:
disabled
inactive
A client that tries to connect gets refused, because nothing is listening on port 22:
ssh: connect to host ws-lab01.local port 22: Connection refused
That error is useful. “Connection refused” means the packet reached the machine and nothing answered. A firewall drop looks different: the client waits and then times out.
Step 2: Enable the SSH server
There are two ways in. Both enable the service at boot and start it now.
From GNOME Settings
Open Settings, scroll the sidebar to System, and pick Secure Shell:

Flip the Secure Shell switch. GNOME asks for your password through polkit, because enabling a system service needs admin rights:

The switch stays on and the dialog shows a suggested login command:

Do not rely on that ssh ws-lab01 line from another machine. Step 4 explains why the bare hostname did not resolve for us and what to use instead.
From the terminal
One command does the same thing:
sudo systemctl enable --now sshd
Confirm it is running and enabled:
systemctl status sshd --no-pager | head -12
The unit is active, enabled, and still marked preset: disabled, which tells you someone switched it on deliberately:
● sshd.service - OpenSSH server daemon
Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; preset: disabled)
Drop-In: /usr/lib/systemd/system/service.d
└─10-timeout-abort.conf
Active: active (running) since Mon 2026-10-05 21:48:47 EAT; 17ms ago
Invocation: 43bd142b1e1645518df590a06265a7e9
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 7265 (sshd)
Tasks: 1 (limit: 9415)
Memory: 1.2M (peak: 1.3M)
CPU: 14ms
The Settings switch did exactly this. After toggling it, systemctl is-enabled sshd returned enabled and the unit list showed the same preset mismatch.
Step 3: Check what the firewall lets in
Do not skip this step. Look at the zone your network interface is in:
firewall-cmd --get-active-zones
firewall-cmd --get-default-zone
sudo firewall-cmd --zone=FedoraWorkstation --list-all
The Workstation zone is active on the interface, allows SSH, and allows every TCP and UDP port from 1025 up:
FedoraWorkstation (default)
interfaces: ens18
FedoraWorkstation
FedoraWorkstation (default, active)
target: default
ingress-priority: 0
egress-priority: 0
icmp-block-inversion: no
interfaces: ens18
sources:
services: dhcpv6-client samba-client ssh
ports: 1025-65535/udp 1025-65535/tcp
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
So you never need to open port 22 on Workstation. You also can’t rely on firewalld to protect anything else you start on a high port. That is a deliberate Workstation default, and it is the reason the remaining steps matter.
The zone is assigned per NetworkManager connection, which gives you a clean way to protect a laptop on untrusted networks. Compare what three built-in zones allow:
for z in public home drop; do printf "%-7s %s\n" $z "$(firewall-cmd --zone=$z --list-services)"; done
public and home still allow SSH. drop allows nothing:
public dhcpv6-client mdns ssh
home dhcpv6-client mdns samba-client ssh
drop
Put the connection you use on public Wi-Fi in the drop zone. List your connections with nmcli connection show and use the exact name. At the laptop’s own keyboard, one command does it:
sudo nmcli connection modify "Cafe Wi-Fi" connection.zone drop
If you change the zone of the connection you are logged in over, schedule an automatic undo first, so a mistake cannot lock you out. That is how we tested it on the lab machine’s wired connection:
sudo systemd-run --on-active=120 --unit=zone-revert nmcli connection modify "Wired connection 1" connection.zone ""
sudo nmcli connection modify "Wired connection 1" connection.zone drop
firewall-cmd --get-active-zones
The change applied immediately, and the established SSH session survived it:
Running timer as unit: zone-revert.timer
Will run service as unit: zone-revert.service
drop
interfaces: ens18
FedoraWorkstation (default)
From the client, the machine vanished completely. The drop zone also blocks mDNS, so even the name stopped resolving:
ssh -o ConnectTimeout=5 [email protected] true
The client could no longer find the host at all:
ssh: Could not resolve hostname ws-lab01.local: Name or service not known
Two minutes later the timer cleared the setting and the interface went back to the default zone. When you want the change to stay, cancel the undo before it fires with sudo systemctl stop zone-revert.timer. On a real laptop, leave the café connection in drop and keep home networks on the default.
Step 4: Connect and verify the host key
Fedora Workstation runs avahi-daemon and has mdns4_minimal from nss-mdns in its hosts: lookup order, so another Workstation can reach yours by its .local name. The name is the Device Name under Settings, Sharing:

Our client was a Fedora Cloud machine, which has neither by default. Install and start them on a client like that:
sudo dnf install nss-mdns
sudo systemctl enable --now avahi-daemon
Then check that the Workstation’s name resolves:
rpm -q nss-mdns avahi; systemctl is-active avahi-daemon; getent hosts ws-lab01.local >/dev/null && echo "ws-lab01.local resolves"
Both packages are present, the daemon runs, and the lookup succeeds:
nss-mdns-0.15.1-29.fc45.x86_64
avahi-0.9~rc2-9.fc45.x86_64
active
ws-lab01.local resolves
The bare name that GNOME Settings suggests did not resolve at all:
resolvectl query ws-lab01
Fedora’s systemd-resolved runs LLMNR in resolve-only mode (resolvectl llmnr prints resolve on both machines), so the Workstation never answers those queries:
ws-lab01: resolve call failed: Name 'ws-lab01' not found
Use the .local name or the IP address. Connect for the first time:
ssh [email protected]
OpenSSH has never seen this host, so it shows the key fingerprint and asks you to confirm it. The output starts with a line saying the authenticity of the host (name and address) can’t be established, followed by:
ED25519 key fingerprint is: SHA256:d742Emul7qUx7cg5PUZVQIBUr0bydQLh01prTjv/ZAM
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Answering yes stores the key in ~/.ssh/known_hosts, and OpenSSH says so in one more line. Do not type yes blindly. That fingerprint is the only thing standing between you and a machine pretending to be yours. Check it on the Workstation itself, at the console:
for k in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf $k; done
The ED25519 line matches what the client printed:
256 SHA256:YQvguj96YRthdQy6sBHGqnZ9D9XZWR9dU8WCIirZBy0 no comment (ECDSA)
256 SHA256:d742Emul7qUx7cg5PUZVQIBUr0bydQLh01prTjv/ZAM no comment (ED25519)
3072 SHA256:aMBnX6qAfK+3i3qf0Sk4iGmxnK616UzZmbpDtA38yg4 no comment (RSA)
Once the fingerprints match, answer yes. The client saves the key in ~/.ssh/known_hosts and never asks again unless the key changes. The verbose client log shows which key exchange was negotiated:
ssh -v -o BatchMode=yes [email protected] true 2>&1 | grep -E "Remote protocol version|kex: algorithm|kex: host key algorithm"
It was the post-quantum hybrid from the defaults:
debug1: Remote protocol version 2.0, remote software version OpenSSH_10.5
debug1: kex: algorithm: mlkem768x25519-sha256
debug1: kex: host key algorithm: ssh-ed25519
Step 5: Switch to key authentication
Generate a key on the client, not on the server. Protect it with a passphrase. A key file without one is a password written to disk:
ssh-keygen -t ed25519 -C "mercy@bastion-01"
The key pair lands in ~/.ssh:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/mercy/.ssh/id_ed25519):
Enter passphrase for "/home/mercy/.ssh/id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/mercy/.ssh/id_ed25519
Your public key has been saved in /home/mercy/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:DQFIXp54lfHDqGf0nk5NHfK7lhroODwwIiCnX55VdQ8 mercy@bastion-01
Copy the public key to the Workstation. This is the last time you type the account password over SSH:
ssh-copy-id [email protected]
The tool logs in with your password and appends the key to ~/.ssh/authorized_keys on the server:
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/mercy/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
[email protected]'s password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh '[email protected]'"
and check to make sure that only the key(s) you wanted were added.
The next login asks for the key’s passphrase instead of the account password:
Enter passphrase for key '/home/mercy/.ssh/id_ed25519':
Step 6: Disable password and root logins
Keep your current SSH session open until this step is finished and tested from a second terminal. If you lock yourself out anyway, the fix is at the Workstation’s own keyboard: delete the file you are about to create and reload sshd.
The Include line near the top of /etc/ssh/sshd_config pulls in every file in /etc/ssh/sshd_config.d/, in name order, before the settings below it. For most settings the first value read wins, so a low number makes sure your file comes first. List settings such as AllowUsers are the exception: every AllowUsers line in any file adds to the list. Create the drop-in:
sudo vim /etc/ssh/sshd_config.d/20-local.conf
Add these three lines, and change mercy to the accounts that should be allowed in:
PasswordAuthentication no
PermitRootLogin no
AllowUsers mercy
Test the syntax before reloading. A broken config plus a reload is how people lose access:
sudo sshd -t && echo "config OK"
sudo systemctl reload sshd
The syntax check passed:
config OK
Then confirm the effective values:
sudo sshd -T | grep -iE "^(passwordauthentication|permitrootlogin|allowusers) "
All three settings took effect:
PermitRootLogin no
PasswordAuthentication no
AllowUsers mercy
From the client, force a password-only login to prove passwords are gone:
ssh -o PubkeyAuthentication=no [email protected]
The server no longer offers password authentication at all:
[email protected]: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
Key logins as mercy still worked. For root, rely on the PermitRootLogin no line in the sshd -T output above: a root login attempt without a key is refused under the old default too, so a client test proves nothing there.
The file number matters more than it looks. On our Fedora Cloud client, cloud-init writes 50-cloud-init.conf with PasswordAuthentication no. We added the opposite setting in a file numbered 99, checked the effective value, then renamed the file to 10 and checked again:
echo "PasswordAuthentication yes" | sudo tee /etc/ssh/sshd_config.d/99-passwords.conf >/dev/null
sudo sshd -T | grep -i ^passwordauthentication
sudo mv /etc/ssh/sshd_config.d/99-passwords.conf /etc/ssh/sshd_config.d/10-passwords.conf
sudo sshd -T | grep -i ^passwordauthentication
sudo rm /etc/ssh/sshd_config.d/10-passwords.conf
The last line removes the test file, because leaving it in place turns password logins back on. The 99 file was ignored, and the 10 file won:
PasswordAuthentication no
PasswordAuthentication yes
Any package or tool that drops a lower-numbered file can silently override yours. Check sshd -T after every change, not the file you edited. For brute-force protection, a non-standard port and the rest of the server hardening, follow the Fedora hardening guide. Changing the port has an SELinux step that catches people out, covered in our guide to changing the SSH port with SELinux. Hardware-backed keys are a further upgrade: see SSH with YubiKey FIDO2 keys.
SSH errors we hit on Fedora and how to fix them
Each of these came up during testing. They are listed by the exact message the client prints.
ssh: connect to host ws-lab01.local port 22: Connection refused
The machine answered, but sshd is not running. This is the Workstation default. Enable the service from Step 2 and check systemctl is-active sshd on the server.
ssh: Could not resolve hostname ws-lab01.local: Name or service not known
Two causes. The client cannot resolve mDNS names (install nss-mdns and enable avahi-daemon, as on our Cloud client), or the server’s interface is in the drop zone, which drops the incoming mDNS queries, so the host never replies. Run firewall-cmd --get-active-zones on the server. If neither applies, connect by IP address.
Permission denied (publickey,gssapi-keyex,gssapi-with-mic)
The list in brackets is what the server will accept. With passwords disabled, only a key works, so the server refuses any client that has no matching key loaded. Check three things: the user is in AllowUsers, the public key is in that user’s ~/.ssh/authorized_keys on the server, and you are offering the right key (add -i ~/.ssh/id_ed25519 to be explicit). ssh -v shows which keys the client tries.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
We reproduced this by deleting the Workstation’s host keys and restarting sshd, which generated new ones. That is exactly what a reinstall does:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:SDmDVpvtwXYE/kUnhBGoMSxpzG6+ug3jiAx6259H9LQ.
Please contact your system administrator.
Add correct host key in /home/mercy/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/mercy/.ssh/known_hosts:3
Host key for ws-lab01.local has changed and you have requested strict checking.
Host key verification failed.
Do not delete the old entry until you know why the key changed. If you reinstalled the machine or regenerated its keys, compare the new fingerprint at the console (Step 4), then remove the stale entry on the client:
ssh-keygen -R ws-lab01.local
Every key stored for that name is removed, and a backup is kept:
# Host ws-lab01.local found: line 1
# Host ws-lab01.local found: line 2
# Host ws-lab01.local found: line 3
/home/mercy/.ssh/known_hosts updated.
Original contents retained as /home/mercy/.ssh/known_hosts.old
If nothing on the server changed, treat the warning as an attack until proven otherwise and do not connect. On a laptop that roams between networks, this is the warning that tells you someone is intercepting the connection, and it only works if you checked the fingerprint the first time.