Introduction to Securing Fedora Servers
Fedora is a popular, cutting-edge Linux distribution that serves as the upstream for Red Hat Enterprise Linux. Its rapid release cycle and modern kernel make it an attractive choice for developers and system administrators running production servers. However, a fresh Fedora installation is not secure by default — it requires deliberate hardening before exposure to the internet.
This tutorial provides a practical, actionable checklist for securing a Fedora server. Whether you are deploying a web server, a database host, or a container runtime, these steps will significantly reduce your attack surface and help you maintain a defensible infrastructure.
Why Server Hardening Matters
Every service you expose is a potential entry point for attackers. Default configurations are often optimized for convenience rather than security. By following a systematic hardening process, you:
- Reduce the attack surface by disabling unnecessary services
- Protect against brute-force authentication attacks
- Limit damage from compromised accounts through privilege isolation
- Ensure auditability through centralized logging
- Maintain compliance with security baselines like CIS or STIG
1. System Updates and Package Management
The first step in securing any server is ensuring all installed packages are up to date. Fedora uses the DNF package manager, which makes it straightforward to apply security patches and enable automatic updates.
Applying Updates Manually
Start by updating the entire system immediately after installation:
# Update all packages
sudo dnf upgrade --refresh
# Check for available security updates only
sudo dnf check-update --security
# Apply only security-related updates
sudo dnf upgrade --security
Enabling Automatic Updates with dnf-automatic
Fedora provides the dnf-automatic package for unattended updates. This is especially useful for security patches that should be applied promptly.
# Install dnf-automatic
sudo dnf install -y dnf-automatic
# Edit the configuration to apply security updates automatically
sudo sed -i 's/^upgrade_type.*/upgrade_type = security/' /etc/dnf/automatic.conf
sudo sed -i 's/^apply_updates.*/apply_updates = yes/' /etc/dnf/automatic.conf
sudo sed -i 's/^emit_via.*/emit_via = motd/' /etc/dnf/automatic.conf
# Enable and start the timer
sudo systemctl enable --now dnf-automatic.timer
The timer runs daily by default. You can verify its schedule with:
systemctl list-timers dnf-automatic.timer
2. User Management and Access Control
Proper user management is foundational to server security. You should never perform routine tasks as the root user. Instead, create dedicated user accounts with sudo privileges and enforce strong authentication policies.
Creating a Sudo User
# Create a new user
sudo adduser deployuser
# Set a strong password
sudo passwd deployuser
# Add the user to the wheel group for sudo access
sudo usermod -aG wheel deployuser
# Verify group membership
groups deployuser
Configuring Sudo Policies
Edit the sudoers file to enforce strict policies, such as requiring password authentication for every sudo command:
# Open the sudoers file safely
sudo visudo -f /etc/sudoers.d/00_custom
# Add the following content
Defaults requiretty
Defaults !visiblepw
Defaults use_pty
Defaults logfile="/var/log/sudo.log"
Defaults passwd_timeout=2
Defaults timestamp_timeout=5
# Allow wheel group members full access with password
%wheel ALL=(ALL) ALL
Disabling Root Login Over SSH
Once you have a working sudo user, disable direct root login to prevent brute-force attacks against the most privileged account:
# Edit the SSH daemon configuration
sudo nano /etc/ssh/sshd_config
# Set the following directives
PermitRootLogin no
3. SSH Hardening
SSH is the primary remote administration protocol for Linux servers, making it a prime target for attackers. Hardening SSH involves key-based authentication, changing default ports, and limiting access.
Setting Up Key-Based Authentication
On your local machine, generate an SSH key pair:
# Generate an ed25519 key (recommended over RSA)
ssh-keygen -t ed25519 -C "admin@yourdomain.com" -f ~/.ssh/fedora_server
# Copy the public key to the server
ssh-copy-id -i ~/.ssh/fedora_server.pub deployuser@server_ip
Hardening the SSH Daemon Configuration
Edit /etc/ssh/sshd_config with the following hardened settings:
# Disable password authentication
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Change the default SSH port (optional but recommended)
Port 2222
# Limit authentication attempts
MaxAuthTries 3
# Set a login grace period
LoginGraceTime 30
# Disable X11 forwarding if not needed
X11Forwarding no
# Allow only specific users
AllowUsers deployuser
# Use strong ciphers and MACs
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
After saving the configuration, validate it before restarting the service:
# Validate the configuration
sudo sshd -t
# Restart the SSH service
sudo systemctl restart sshd
4. Firewall Configuration with firewalld
Fedora ships with firewalld, a dynamic firewall manager that uses zones to define trust levels for network connections. A properly configured firewall is essential for controlling inbound traffic.
Basic Firewall Setup
# Ensure firewalld is installed and running
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
# Check the default zone
sudo firewall-cmd --get-default-zone
# List all active rules
sudo firewall-cmd --list-all
Configuring Zones and Services
For a typical web server, you only need to allow SSH, HTTP, and HTTPS traffic:
# If you changed the SSH port, allow the custom port
sudo firewall-cmd --permanent --add-port=2222/tcp
# Allow web traffic
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
# Remove unnecessary services
sudo firewall-cmd --permanent --remove-service=cockpit
sudo firewall-cmd --permanent --remove-service=dhcpv6-client
# Reload the firewall to apply changes
sudo firewall-cmd --reload
# Verify the final configuration
sudo firewall-cmd --list-all
Using Rich Rules for Advanced Control
Rich rules allow you to create more granular firewall policies, such as rate limiting:
# Limit SSH connections to 2 per minute from any source
sudo firewall-cmd --permanent --add-rich-rule='rule service name=ssh limit value=2/m accept'
# Allow SSH only from a specific IP range
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=192.168.1.0/24 port port=2222 protocol=tcp accept'
# Drop all other SSH traffic
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 port port=2222 protocol=tcp drop'
sudo firewall-cmd --reload
5. SELinux Configuration
SELinux (Security-Enhanced Linux) is enabled by default on Fedora and provides mandatory access control (MAC). While it can be complex, disabling SELinux is strongly discouraged. Instead, learn to configure it properly.
Checking SELinux Status
# Check current SELinux status
sestatus
# Check the current mode
getenforce
# View SELinux booleans related to common services
getsebool -a | grep -E 'httpd|ssh|ftp'
Configuring SELinux for Web Servers
If you are running a web server with non-standard document roots, you need to set the correct SELinux contexts:
# Set the httpd context for a custom web directory
sudo semanage fcontext -a -t httpd_sys_content_t '/var/www/mysite(/.*)?'
sudo restorecon -Rv /var/www/mysite
# Allow httpd to connect to a database
sudo setsebool -P httpd_can_network_connect_db on
# Allow httpd to make outbound network connections
sudo setsebool -P httpd_can_network_connect on
# Allow httpd to read user home directories (use with caution)
sudo setsebool -P httpd_read_user_content on
Troubleshooting SELinux Denials
# Install troubleshooting tools
sudo dnf install -y setroubleshoot-server
# View recent SELinux denials
sudo ausearch -m AVC,USER_AVC -ts recent
# Generate a report of denials with suggested fixes
sudo sealert -a /var/log/audit/audit.log
6. Fail2Ban for Intrusion Prevention
Fail2Ban monitors log files for repeated failed authentication attempts and temporarily bans offending IP addresses using firewall rules. It is an effective defense against brute-force attacks.
Installing and Configuring Fail2Ban
# Install Fail2Ban
sudo dnf install -y fail2ban
# Create a local configuration file
sudo nano /etc/fail2ban/jail.local
Add the following configuration to jail.local:
[DEFAULT]
# Ban duration in seconds (1 hour)
bantime = 3600
# Time window for counting failures (10 minutes)
findtime = 600
# Number of failures before ban
maxretry = 3
# Email notifications
destemail = admin@yourdomain.com
sender = fail2ban@yourdomain.com
action = %(action_)s
[sshd]
enabled = true
port = 2222
logpath = %(sshd_log)s
backend = systemd
maxretry = 3
bantime = 3600
Enable and start the Fail2Ban service:
# Enable and start Fail2Ban
sudo systemctl enable --now fail2ban
# Check the status of all jails
sudo fail2ban-client status
# Check the SSH jail specifically
sudo fail2ban-client status sshd
# Manually unban an IP address
sudo fail2ban-client set sshd unbanip 192.168.1.100
7. Securing Network Services
Beyond SSH and the firewall, individual network services require their own hardening. Here are configurations for common services.
Securing Nginx
# Install Nginx
sudo dnf install -y nginx
# Edit the main configuration
sudo nano /etc/nginx/nginx.conf
Add the following security headers in the http block:
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2 default_server;
server_name yourdomain.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Hide server version
server_tokens off;
# Disable unnecessary HTTP methods
if ($request_method !~ ^(GET|POST|HEAD|OPTIONS)$ ) {
return 405;
}
location / {
root /var/www/html;
index index.html index.htm;
}
}
Securing PostgreSQL
# Edit PostgreSQL configuration
sudo nano /var/lib/pgsql/data/pg_hba.conf
# Restrict connections to localhost only
# TYPE DATABASE USER ADDRESS METHOD
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
# Edit the main PostgreSQL configuration
sudo nano /var/lib/pgsql/data/postgresql.conf
# Set the following directives
listen_addresses = 'localhost'
password_encryption = scram-sha-256
ssl = on
log_connections = on
log_disconnections = on
# Restart PostgreSQL
sudo systemctl restart postgresql
8. Kernel and System Hardening
The Linux kernel exposes many tunable parameters that affect security. Fedora uses sysctl for runtime configuration.
Applying Sysctl Security Parameters
# Create a custom sysctl configuration file
sudo nano /etc/sysctl.d/99-security.conf
Add the following hardening parameters:
# Disable IP forwarding (unless this is a router)
net.ipv4.ip_forward = 0
# Disable packet redirect
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Enable reverse path filtering
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Log suspicious packets (martians)
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Disable ICMP redirect acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Disable source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable TCP SYN cookies
net.ipv4.tcp_syncookies = 1
# Ignore ICMP broadcast requests (smurf attacks)
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Restrict kernel pointers in /proc
kernel.kptr_restrict = 2
# Restrict access to dmesg
kernel.dmesg_restrict = 1
# Restrict access to kernel performance events
kernel.perf_event_paranoid = 2
# Enable ASLR
kernel.randomize_va_space = 2
# Restrict unprivileged use of BPF
kernel.unprivileged_bpf_disabled = 1
# Restrict user namespaces
kernel.unprivileged_userns_clone = 0
# Protect hardlinks and symlinks
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2
Apply the changes immediately:
sudo sysctl --system
9. Audit and Logging
Comprehensive logging is essential for detecting security incidents and performing forensic analysis. Fedora includes rsyslog and auditd for system and security auditing.
Configuring Auditd
# Install and enable auditd
sudo dnf install -y audit
sudo systemctl enable --now auditd
# Edit the audit configuration
sudo nano /etc/audit/auditd.conf
Configure the following settings:
log_file = /var/log/audit/audit.log
log_format = RAW
log_group = root
priority_boost = 4
flush = INCREMENTAL_ASYNC
freq = 50
max_log_file = 100
num_logs = 5
max_log_file_action = ROTATE
space_left = 75
space_left_action = EMAIL
action_mail_acct = root
admin_space_left = 50
admin_space_left_action = HALT
disk_full_action = HALT
disk_error_action = HALT
Adding Audit Rules
Create custom audit rules to monitor critical system files and activities:
# Create a custom rules file
sudo nano /etc/audit/rules.d/99_custom.rules
# Add the following rules
# Monitor changes to /etc/passwd
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/security/opasswd -p wa -k identity
# Monitor sudoers files
-w /etc/sudoers -p wa -k scope
-w /etc/sudoers.d/ -p wa -k scope
# Monitor SSH configuration
-w /etc/ssh/sshd_config -p wa -k ssh_config
# Monitor login and logout events
-w /var/log/lastlog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
# Monitor system time changes
-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change
-w /etc/localtime -p wa -k time-change
# Monitor kernel module loading
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=b64 -S init_module,delete_module -k modules
# Restart auditd to apply rules
sudo systemctl restart auditd
Sending Logs to a Remote Server
For production environments, forward logs to a centralized logging server to prevent tampering:
# Edit rsyslog configuration
sudo nano /etc/rsyslog.conf
# Add at the end of the file
*.* @@logserver.yourdomain.com:6514
# Configure TLS for secure log transmission
$DefaultNetstreamDriver gtls
$DefaultNetstreamDriverCAFile /etc/pki/rsyslog/ca.pem
$DefaultNetstreamDriverCertFile /etc/pki/rsyslog/client-cert.pem
$DefaultNetstreamDriverKeyFile /etc/pki/rsyslog/client-key.pem
$ActionSendStreamDriverMode 1
$ActionSendStreamDriverAuthMode x509/name
$ActionSendStreamDriverPermittedPeer logserver.yourdomain.com
sudo systemctl restart rsyslog
10. Automated Vulnerability Scanning
Regular vulnerability scanning helps identify weaknesses before attackers do. OpenSCAP is the standard tool for scanning Fedora systems against security baselines.
Running a Security Scan with OpenSCAP
# Install OpenSCAP and SCAP security guide
sudo dnf install -y openscap-utils scap-security-guide
# Find available scan profiles
ls /usr/share/xml/scap/ssg/content/
# Run a scan against the OSPP (Protection Profile) baseline
sudo oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_ospp \
--results scan-results.xml \
--report scan-report.html \
/usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml
# View the HTML report
sudo cp scan-report.html /var/www/html/
Generating a Remediation Script
# Generate a bash script to fix identified issues
sudo oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_ospp \
--remediate \
--results remediation-results.xml \
--report remediation-report.html \
/usr/share/xml/scap/ssg/content/ssg-fedora-ds.xml
11. Regular Maintenance Checklist
Security is not a one-time task but an ongoing process. Create a maintenance schedule to ensure your server remains secure over time.
Weekly Tasks
- Review security update notifications and apply patches
- Check Fail2Ban banned IP lists for patterns of attack
- Review audit logs for suspicious activity
- Verify backup integrity and test restoration procedures
Monthly Tasks
- Run OpenSCAP vulnerability scans and review reports
- Review user accounts and remove inactive ones
- Audit SSH authorized_keys files for unauthorized additions
- Review firewall rules and remove unnecessary entries
- Check SSL/TLS certificate expiration dates
Quarterly Tasks
- Perform a full security audit using an external tool like Lynis
- Review and update incident response procedures
- Test disaster recovery procedures end-to-end
- Review SELinux policies and audit denials
- Rotate SSH keys for all administrative users
Running Lynis for Comprehensive Auditing
# Install Lynis
sudo dnf install -y lynis
# Run a full system audit
sudo lynis audit system
# View the report
sudo cat /var/log/lynis-report.dat
# Run with warnings and suggestions only
sudo lynis audit system --quiet
12. Additional Hardening Measures
Disabling Unnecessary Services
# List all enabled services
sudo systemctl list-unit-files --type=service --state=enabled
# Disable services you do not need
sudo systemctl disable --now avahi-daemon
sudo systemctl disable --now cups
sudo systemctl disable --now bluetooth
sudo systemctl disable --now ModemManager
# Mask services to prevent them from being started manually
sudo systemctl mask avahi-daemon
Configuring Automatic Time Synchronization
# Enable chronyd for NTP synchronization
sudo dnf install -y chrony
sudo systemctl enable --now chronyd
# Verify synchronization
chronyc tracking
chronyc sources
Setting Up AIDE for File Integrity Monitoring
# Install AIDE
sudo dnf install -y aide
# Initialize the AIDE database
sudo aide --init
# Move the database to the proper location
sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
# Run a manual check
sudo aide --check
# Set up a daily cron job for integrity checks
echo "0 3 * * * root /usr/sbin/aide --check | mail -s 'AIDE Report' admin@yourdomain.com" | sudo tee /etc/cron.d/aide
Limiting Process and Resource Usage
Configure resource limits to prevent denial-of-service attacks from consuming all system resources:
# Edit limits configuration
sudo nano /etc/security/limits.conf
# Add the following limits
* soft nofile 65535
* hard nofile 65535
* soft nproc 4096
* hard nproc 8192
* soft core 0
* hard core 0
# Configure systemd resource limits for specific services
sudo systemctl edit nginx.service
In the systemd override file:
[Service]
LimitNOFILE=65535
LimitNPROC=4096
MemoryMax=2G
CPUQuota=200%
TasksMax=512
Conclusion
Securing a Fedora server requires a multi-layered approach that addresses system updates, access control, network filtering, kernel hardening, and continuous monitoring. By following this practical checklist, you establish a strong security baseline that protects against common attack vectors while maintaining the flexibility and performance that make Fedora an excellent server platform. Remember that security is an ongoing process — regularly review your configurations, apply updates promptly, audit your systems, and stay informed about emerging threats. The time invested in hardening your server today will pay dividends in preventing costly security incidents tomorrow.