You just finished installing Fedora Workstation
You just finished installing Fedora Workstation. The desktop looks clean, your terminal is configured, and you are ready to work. Then you remember that default configurations prioritize convenience over security. You want to lock the system down without turning your daily driver into a headless server that breaks every time you plug in a USB drive. You need a practical hardening path that respects Fedora's architecture and keeps your workflow intact.
What is actually happening
Hardening a workstation is about shrinking the attack surface. Fedora already ships with strong defaults. SELinux runs in enforcing mode. firewalld blocks unsolicited inbound traffic. The package manager signs every update. Your job is not to rebuild these tools from scratch. Your job is to verify they are active, remove unnecessary services, and enforce strict access controls. Think of it like securing a house. You do not need a vault door on the kitchen window. You just need to lock the front door, check the deadbolt, and make sure the alarm is armed. The underlying security model relies on defense in depth. If one layer fails, the next one catches the breach. Your configuration steps simply ensure every layer is actually engaged.
The fix or how-to
Start with the package manager. Unpatched software is the easiest entry point for an attacker. Fedora's release cadence means security fixes arrive quickly, but they only help if you apply them.
Here is how to force the package manager to fetch fresh metadata and apply every available update.
sudo dnf upgrade --refresh
# --refresh forces dnf to ignore cached repo metadata
# ensures you get the latest security advisories immediately
# upgrade pulls all available package updates for the current release
Convention aside: dnf upgrade --refresh is your weekly maintenance command. It forces the package manager to check for new metadata instead of trusting the cached version. Do not confuse this with dnf system-upgrade, which is reserved for crossing major release boundaries like 40 to 42.
Automatic updates remove the human delay between a CVE disclosure and patch application. Fedora provides a dedicated timer for this.
Here is how to install the automatic update daemon and configure it to apply security fixes without interrupting your work.
sudo dnf install dnf-automatic
# installs the background update service and timer units
# creates the default configuration file in /etc/dnf/
sudo sed -i 's/apply_updates = no/apply_updates = yes/' /etc/dnf/automatic.conf
# enables automatic installation of updates instead of just downloading them
# targets the persistent config file in /etc/ to survive package upgrades
sudo systemctl enable --now dnf-automatic-install.timer
# starts the timer immediately and registers it for boot
# the timer checks twice daily and applies updates when idle
Convention aside: Automatic updates on a workstation require careful tuning. You want security patches to apply silently, but you do not want a kernel update to reboot your machine while you are in the middle of a video call. The timer handles this by checking for updates twice daily and applying them only when the system is idle.
Run the refresh command before you debug. Half the time the symptom is a known CVE that already has a fix in the repos.
Fedora enables SELinux by default. Some guides tell you to disable it because it blocks things. That advice is dangerous. SELinux enforces mandatory access controls that survive a root compromise. If a web server gets pwned, SELinux stops it from reading your home directory.
Here is how to verify the current SELinux state and force it back to enforcing if it was accidentally toggled.
getenforce
# prints the runtime SELinux mode
# should return Enforcing on a default Fedora installation
sudo setenforce 1
# switches the runtime policy to enforcing immediately
# does not require a reboot but only lasts until the next boot
sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
# updates the persistent configuration file
# ensures the policy survives reboots and kernel updates
Convention aside: SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before you toggle the policy to permissive. The system is telling you exactly which process tried to cross a boundary.
Trust the policy. Manual file edits drift, SELinux contexts stay.
firewalld manages your network boundaries. The default public zone drops everything except SSH and DHCPv6. You probably do not need DHCPv6 client exposure on a desktop.
Here is how to set the default zone, remove unnecessary services, and apply the changes to the running firewall.
sudo firewall-cmd --set-default-zone=public
# sets the public zone as the default for all new interfaces
# public zone drops inbound traffic except explicitly allowed services
sudo firewall-cmd --permanent --remove-service=dhcpv6-client
# removes the DHCPv6 client service from the permanent configuration
# reduces the attack surface for network enumeration tools
sudo firewall-cmd --reload
# applies the permanent configuration to the runtime firewall
# required after every --permanent change to avoid config drift
sudo firewall-cmd --list-all
# prints the active zone configuration for verification
# shows interfaces, sources, ports, and allowed services
Convention aside: Always run firewall-cmd --reload after a permanent change. Otherwise the runtime configuration and the persistent configuration diverge, and you will wonder why your rules disappear after a reboot.
Next, trim the background services. Every enabled daemon is a potential vector.
Here is how to list every service that starts at boot and disable the ones you do not use.
systemctl list-unit-files --state=enabled
# prints all systemd units configured to start automatically
# helps you identify legacy services or unused daemons
sudo systemctl disable --now cups
# stops the service immediately and removes boot symlinks
# --now combines disable and stop into a single atomic operation
Disable what you do not use. A quiet system is a secure system.
Local accounts need strict policies. Password aging prevents credential reuse. Root access should be restricted to sudo with explicit logging.
Here is how to enforce password rotation and validate your privilege escalation configuration.
sudo chage -M 90 -W 14 username
# sets maximum password age to 90 days
# triggers a 14-day warning before the password expires
sudo passwd root
# sets a strong password for the root account
# required for emergency recovery if sudo breaks
sudo visudo -c
# checks the sudoers file for syntax errors
# prevents lockout by validating rules before they take effect
Convention aside: Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/. The visudo command catches syntax errors before they lock you out of privilege escalation.
If you run SSH, harden the daemon. Password authentication is vulnerable to brute force. Key-based auth is the standard.
Here is how to disable root login and password authentication in the SSH daemon configuration.
sudo sed -i 's/^#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
# comments out the default and enforces key-only root access
# prevents direct root brute force attacks
sudo sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
# disables password logins for all users
# forces the use of SSH keys or certificate authentication
sudo systemctl restart sshd
# reloads the daemon with the new configuration
# drops existing connections and applies rules to new sessions
Restart the daemon after every config change. Verify the new rules are active before closing the terminal.
Manual checks miss details. OpenSCAP automates compliance verification against established benchmarks like CIS or DISA STIG.
Here is how to install the scanner and run a baseline compliance check against the standard Fedora profile.
sudo dnf install openscap-scanner scap-security-guide
# installs the evaluation engine and the official security guide
# provides the XML data streams required for scanning
sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_standard \
--report /tmp/report.html \
/usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml
# runs the evaluation against the standard compliance profile
# generates an HTML report with pass/fail status and remediation steps
Run the scanner monthly. Export the HTML report and file it for your records.
Verify it worked
You need to confirm each layer is active. A hardening checklist is useless if you cannot prove the controls are holding.
Here is how to check the status of your security layers in a single verification pass.
getenforce
# confirms SELinux is in enforcing mode
# should output Enforcing without warnings
sudo firewall-cmd --list-all
# verifies the public zone is active and unnecessary services are gone
# check the services line for a minimal list
systemctl is-enabled dnf-automatic-install.timer
# confirms the automatic update timer is registered for boot
# should return enabled
sudo -l
# lists your sudo privileges and password requirements
# ensures you can still escalate when needed
Check the status before you restart. A green active state means the layer is holding.
Common pitfalls and what the error looks like
Hardening breaks convenience. You will hit SELinux denials when a new app tries to access a non-standard directory. You will lock yourself out of SSH if you disable passwords before testing key authentication. You will break network connectivity if you remove the wrong firewalld service. The fix is always the same. Test in a virtual machine first. Keep a second terminal session open when changing SSH or sudo policies. Revert the change if the system stops responding.
If you misconfigure SSH and attempt to connect, the daemon will reject your credentials and log the failure. You will see this in the journal:
sshd[1234]: Failed password for invalid user admin from 192.168.1.50 port 54321 ssh2
sshd[1234]: Connection closed by authenticating user admin 192.168.1.50 port 54321 [preauth]
The error means your key authentication is not working or the authorized_keys file has incorrect permissions. Check ~/.ssh/authorized_keys permissions with chmod 600. Verify the daemon config with sshd -t. Never close your active session until you have confirmed a new connection succeeds.
Snapshot the system before the upgrade. Future-you will thank you.
When to use this vs alternatives
Use this checklist when you are running a daily driver that handles sensitive documents or connects to untrusted networks. Use a minimal server installation when you are deploying headless workloads and only need specific daemons. Use Silverblue when you want a known-good base image you can always roll back to. Stay on the upstream Workstation if you only deviate from the defaults occasionally.
Pick the tool that matches your threat model. Over-engineering creates more bugs than it fixes.