Posted to Infrastructure on

Hardened Local Administration with OpenSSH and firewalld

Build a narrow LAN administration path from a trusted workstation, then prove both the allowed public-key path and the password path you intend to deny.

#fedora#linux#local-server#openssh#ssh#firewalld#security

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.

Footnotes

Scroll to article content with footnote reference 2

The official firewalld rich-language reference documents source CIDR matching, services, and actions in rich rules, including examples that combine a source network with a named service.

Scroll to article content with footnote reference 2
Scroll to article content with footnote reference 3

The OpenBSD/OpenSSH ssh-keygen(1) manual is the upstream reference for Ed25519 key generation, passphrases, fingerprints, and known_hosts maintenance operations.

Scroll to article content with footnote reference 3
Scroll to article content with footnote reference 4

The upstream sshd_config(5) manual defines PermitRootLogin, PubkeyAuthentication, PasswordAuthentication, KbdInteractiveAuthentication, AllowUsers, and AllowTcpForwarding. This is the authoritative source for what those directives actually mean.

Scroll to article content with footnote reference 4 Scroll to article content with footnote reference 4
Scroll to article content with footnote reference 5

The upstream ssh_config(5) manual documents client-side aliases and identity selection, including Host, HostName, User, IdentityFile, and IdentitiesOnly.

Scroll to article content with footnote reference 5
Scroll to article content with footnote reference 6

OpenSSH documents host-key storage and verification across ssh(1), ssh-keygen(1), and ssh_config(5). A changed host key can be benign after deliberate reinstallation or key rotation, but it is also one of SSH's primary warnings for a server-identity mismatch.

Scroll to article content with footnote reference 6

Down the rabbit hole

Fedora Local Server Guide

Posted to Infrastructure on

KVM, libvirt, and the First Fedora Server VM

Posted to Infrastructure on
Browse all posts

Map the system you need

Describe the product, workflow, integration, or operation you are building—and where the current system loses power. Include the people, tools, boundaries, constraints, and outcome that matter.

Every message is reviewed. Responses are limited.

Press Escape to close preview popover.