Remote Access Should Start Narrow
Once Fedora is stable locally, the next goal is to manage it from another trusted computer without opening SSH broadly to the network or depending on passwords.
This guide assumes you completed Architecture, Hardware, and Fedora Installation and have a Fedora host that boots cleanly with SELinux enforcing and firewalld active.
The path is deliberately small:
trusted admin workstation
│
│ SSH with dedicated Ed25519 key
▼
Fedora host
│
├── root SSH login disabled
├── password SSH login disabled
├── only named admin user allowed
└── firewalld allows SSH only from trusted LAN subnet
The examples use a Mac as the admin workstation, but Linux follows the same OpenSSH model except for macOS Keychain-specific options.
Example values: Hostnames, usernames, addresses, interface names, device paths, and other identifiers are illustrative unless explicitly described as software defaults. Discover the values on your own systems before running commands.
Know Which Network You Are Trusting
On the Fedora host, inspect both addresses and routes:
ip -br address
ip route
A fictional LAN might look like:
eno2 UP 192.168.50.50/24
default via 192.168.50.1 dev eno2
That implies a 192.168.50.0/24 LAN. Your network may use a completely different range. The point is to identify the interface and source network you actually intend to trust before writing firewall policy around them.
Give the Wired Interface Its Own Firewall Policy
Fedora's NetworkManager connection profiles can carry a connection.zone property that associates a connection with a firewalld trust zone.1 This keeps the physical server interface on an explicit policy instead of whatever default zone happens to be active.
First, list the NetworkManager profiles and find the one attached to the wired interface you identified above:
nmcli connection show
The commands below assume that profile is named Wired connection 1. Replace the name with your own profile before running them.
Create the new zone and load it into firewalld before changing the active connection:
sudo firewall-cmd --permanent --new-zone=local-server
sudo firewall-cmd --reload
A new zone does not admit SSH until you add a rule for it. Before binding the wired profile to local-server, add and verify the source-restricted SSH rule while the interface is still using its current firewall policy:
sudo firewall-cmd --permanent --zone=local-server --add-rich-rule='rule family="ipv4" source address="192.168.50.0/24" service name="ssh" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --zone=local-server --list-all
Confirm that the displayed rich rule contains the LAN source CIDR you intend to trust. Only then bind the wired profile to the new zone:
sudo nmcli connection modify "Wired connection 1" connection.zone local-server
NetworkManager documents connection.zone as the firewall trust level for that connection.1:1 Treat a change to the zone of an active management connection as immediately security-relevant: keep the current authenticated session or a physical console available until a fresh SSH connection succeeds.
If you are at the physical console, reconnect the profile if necessary:
sudo nmcli connection down "Wired connection 1" && sudo nmcli connection up "Wired connection 1"
If you are making the change remotely, do not bounce the connection. Verify the active zone and its SSH rule from the session you kept open:
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=local-server --list-all
A successful binding shows the wired interface under local-server, with the intended source-restricted SSH rule already present. If either part is wrong, use the existing session or console to correct it before testing a fresh login.
SSH Should Be Reachable Only from the LAN You Chose
The local-server zone now has one deliberate SSH opening: the rich rule added before the interface was bound to the zone. firewalld's rich-language grammar explicitly supports combining a source CIDR with a service and an action in one rule.2
A minimal final zone may resemble:
local-server (active)
interfaces: eno2
services:
ports:
forward: no
masquerade: no
rich rules:
rule family="ipv4" source address="192.168.50.0/24" service name="ssh" accept
The empty services: line is fine. The rich rule is narrower than adding ssh to the whole zone.
Make the SSH Server State Explicit
Fedora may already have the server package installed. Make the intended state explicit and check what systemd reports:
sudo dnf install openssh-server
sudo systemctl enable --now sshd
systemctl is-enabled sshd
systemctl is-active sshd
sudo ss -lntp | grep ':22'
You want sshd enabled, active, and listening on TCP 22 before you harden authentication.
Give This Server Its Own Client Identity
Create a key specifically for this server instead of reusing one identity everywhere:
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/local_server_ed25519 -C "admin-workstation-to-local-server"
OpenSSH's ssh-keygen supports Ed25519 keys directly and documents the key-generation and passphrase behavior used here.3
This creates:
~/.ssh/local_server_ed25519 private key
~/.ssh/local_server_ed25519.pub public key
The private key stays on the admin workstation. Only the public key belongs on the server.
Load it into the local agent with the platform-appropriate command:
# macOS
ssh-add --apple-use-keychain ~/.ssh/local_server_ed25519
# Linux with an SSH agent
ssh-add ~/.ssh/local_server_ed25519
Use a passphrase unless you have chosen another key-protection model for a specific reason.
Prove Key Authentication Before Removing Passwords
If password SSH is still temporarily available, install the public key:
ssh-copy-id -i ~/.ssh/local_server_ed25519.pub admin@192.168.50.50
If ssh-copy-id is unavailable, append the .pub contents to ~/.ssh/authorized_keys, then set conservative permissions:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Test the new identity explicitly:
ssh -i ~/.ssh/local_server_ed25519 admin@192.168.50.50
Do not disable password authentication until this succeeds from a fresh terminal. A working session you opened before the change is not enough evidence.
Keep Server Authentication Policy Explicit
Keep local policy separate from the distribution's main configuration:
sudo nano /etc/ssh/sshd_config.d/10-local-server.conf
Use:
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
AllowUsers admin
OpenSSH documents each of these controls directly: PermitRootLogin, PasswordAuthentication, KbdInteractiveAuthentication, PubkeyAuthentication, and AllowUsers are server-side policy directives, not Fedora-specific conventions.4
AllowUsers admin must name the administrator account whose key you just tested. If it names a different or nonexistent account, sshd rejects every SSH login after the restart.
If this host will later serve as a ProxyJump path, keep TCP forwarding available:
AllowTcpForwarding yes
OpenSSH permits TCP forwarding by default and lets you limit or disable it with AllowTcpForwarding; the right setting depends on whether the jump-host function belongs in your design.4:1
Keep an existing console or authenticated session open. Validate the configuration, and restart only if validation succeeds:
sudo sshd -t && sudo systemctl restart sshd
sshd -t prints nothing when the configuration parses. If it prints an error, the restart does not run; fix the drop-in and validate again. After a successful restart, test a fresh login before you close the existing session.
Test the Path You Intend to Deny
Reading the configuration file is not enough. Test the unwanted authentication path from the admin workstation:
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password,keyboard-interactive admin@192.168.50.50
A correct hardened configuration rejects that attempt instead of producing a usable password login.
Then prove the intended path still works:
ssh -i ~/.ssh/local_server_ed25519 admin@192.168.50.50
Security configuration is stronger when you validate both the allowed path and the path that should now be denied.
Give the Connection a Stable Local Name
On macOS or Linux, edit ~/.ssh/config. A macOS entry might look like:
Host local-server-lan
HostName 192.168.50.50
User admin
IdentityFile ~/.ssh/local_server_ed25519
IdentitiesOnly yes
AddKeysToAgent yes
UseKeychain yes
Host, HostName, User, IdentityFile, and IdentitiesOnly are standard OpenSSH client configuration options.5 UseKeychain is macOS-specific and should be omitted elsewhere.
Inspect what OpenSSH will actually use before connecting:
ssh -G local-server-lan | grep -E '^(hostname|user|identityfile) '
ssh local-server-lan
Host Keys Are the Server's Identity
The first time a client reaches a server name or address, SSH asks whether to trust the server's host key. Inspect the server's ED25519 host-key fingerprint locally:
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Compare that fingerprint with what the client presents before accepting it. If the same server later moves to another address, compare fingerprints again instead of reflexively deleting warnings.
If an obsolete address entry truly needs to be removed:
ssh-keygen -R 192.168.50.50
The known_hosts mechanism is a server-authentication boundary. A changed host key is something to explain, not something to silence.6
Keep an Independent Recovery Path
Public-key-only SSH makes the private key part of the access boundary. If the workstation is wiped and that private key is gone, LAN or Tailscale connectivity alone cannot recreate SSH authentication.
Preserve at least one independent recovery path:
- physical keyboard/monitor on the Fedora host;
- a working local Fedora account;
- the ability to add a replacement public key to
authorized_keys.
A fuller recovery and secrets architecture belongs in a separate guide. The rule here is simpler: do not remove every fallback just because the primary path works today.
A Reboot Is the First Real Persistence Test
Reboot the server:
sudo reboot
If the root disk is LUKS-encrypted, unlock it locally when prompted. After Fedora returns, reconnect from the admin workstation:
ssh local-server-lan
Once connected to the Fedora host, verify the complete server-side state:
systemctl is-enabled sshd
systemctl is-active sshd
sudo firewall-cmd --zone=local-server --list-all
getenforce
The resulting state is:
sshd enabled + active
SELinux Enforcing
wired interface local-server zone
SSH allowed only from intended LAN source
password SSH disabled
root SSH disabled
Temporary LANs Should Stay Temporary
If the current router is only an interim network edge, avoid building permanent Fedora-side addressing around it.
A DHCP address is enough during the build. A later router or firewall can own the final reservation and addressing plan. The Tailscale guide also reduces routine administration's dependence on the current LAN address.