Linux Security Fundamentals
As a DevOps engineer, securing your servers is as critical as deploying them. A default Linux installation requires hardening before exposure to any network.
Firewall Fundamentals
A firewall filters network traffic based on rules. Linux's packet filtering framework is netfilter, managed through frontend tools.
UFW — Uncomplicated Firewall
UFW is the default firewall for Ubuntu/Debian. It simplifies iptables.
# Check UFW status
$ sudo ufw status verbose
Status: inactive
# Enable UFW (always allow SSH first!)
$ sudo ufw allow 22/tcp comment 'SSH access'
$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation? y
Firewall is active and enabled on startup
# After enabling, verify SSH is allowed
$ sudo ufw status verbose
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere # SSH access
80/tcp ALLOW Anywhere # HTTP
443/tcp ALLOW Anywhere # HTTPSCommon UFW Commands
# Allow SSH (by service name)
$ sudo ufw allow ssh
# Allow by port number
$ sudo ufw allow 22/tcp
$ sudo ufw allow 80/tcp
# Allow from specific IP
$ sudo ufw allow from 192.168.1.100
# Allow from subnet
$ sudo ufw allow from 192.168.1.0/24
# Allow to specific port from anywhere
$ sudo ufw allow 8080/tcp
# Deny a connection
$ sudo ufw deny from 192.168.1.100
# Delete a rule
$ sudo ufw delete allow 80/tcp
# Allow established connections (for outgoing traffic)
$ sudo ufw allow out on eth0
# Show numbered rules
$ sudo ufw status numbered
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 80/tcp ALLOW IN Anywhere
# Delete rule by number
$ sudo ufw delete 2Application Profiles
UFW ships with profiles for common apps:
# List application profiles
$ sudo ufw app list
Available applications:
Apache
Apache Full
Apache Secure
CUPS
Dovecot
Nginx Full
Nginx HTTP
Nginx HTTPS
OpenSSH
Postfix
Postfix SMTPS
Postfix Submission
# Allow using a profile
$ sudo ufw allow 'Nginx Full'
# View profile details
$ sudo ufw app info 'Nginx Full'
Profile: Nginx Full
Title: Web Server (Nginx, HTTP + HTTPS)
Description: Full Nginx Web Server
Ports:
80,443/tcpUFW with Docker
# If using Docker, Docker manages its own firewall rules
# UFW and Docker can conflict
# Option 1: Allow Docker to manage iptables
# In /etc/docker/daemon.json:
# { "iptables": true }
# Option 2: Configure UFW for Docker
# In /etc/default/ufw:
# DEFAULT_FORWARD_POLICY="ACCEPT"Firewalld — RHEL/CentOS/Fedora
Firewalld uses zones to define trust levels for network connections.
# Check if running
$ sudo firewall-cmd --state
running
# Get active zones
$ sudo firewall-cmd --get-active-zones
public
interfaces: eth0
# List all zones
$ sudo firewall-cmd --get-zones
# List services in current zone
$ sudo firewall-cmd --list-services
dhcpv6-client ssh
# List ports in current zone
$ sudo firewall-cmd --list-portsManaging Services
# Allow HTTP and HTTPS permanently
$ sudo firewall-cmd --zone=public --add-service=http --permanent
$ sudo firewall-cmd --zone=public --add-service=https --permanent
$ sudo firewall-cmd --reload
# Allow a custom port
$ sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
$ sudo firewall-cmd --reload
# Remove a service
$ sudo firewall-cmd --zone=public --remove-service=http --permanent
$ sudo firewall-cmd --reload
# Allow SSH from specific IP
$ sudo firewall-cmd --zone=trusted --add-source=192.168.1.0/24 --permanent
$ sudo firewall-cmd --zone=trusted --add-service=ssh --permanent
$ sudo firewall-cmd --reloadCreating a Custom Zone
# Create a zone for web traffic
$ sudo firewall-cmd --new-zone=webserver --permanent
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --zone=webserver --add-service=http --permanent
$ sudo firewall-cmd --zone=webserver --add-service=https --permanent
$ sudo firewall-cmd --zone=webserver --add-interface=eth1 --permanentIptables — The Classic Tool
iptables is the foundational firewall tool. UFW and Firewalld are abstractions over it.
# List current rules
$ sudo iptables -L -n -v
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
DROP all -- 0.0.0.0/0 0.0.0.0/0
# Allow incoming SSH
$ sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Allow HTTP/HTTPS
$ sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Drop all other incoming traffic
$ sudo iptables -A INPUT -j DROP
# Save rules (Ubuntu)
$ sudo netfilter-persistent saveChains:
| Chain | Purpose |
|---|---|
INPUT | Incoming traffic to the host |
OUTPUT | Outgoing traffic from the host |
FORWARD | Traffic being routed through the host |
Targets:
| Target | Effect |
|---|---|
ACCEPT | Allow the packet |
DROP | Silently discard the packet |
REJECT | Discard and send error response |
Mandatory Access Control — SELinux & AppArmor
Standard Linux permissions (rwx) are Discretionary Access Control (DAC). MAC systems provide additional enforcement.
SELinux (RHEL/CentOS)
# Check SELinux status
$ getenforce
Enforcing
# Modes:
# Enforcing - SELinux policy is enforced
# Permissive - SELinux logs but doesn't block
# Disabled - SELinux is off
# Temporarily set to permissive (for troubleshooting)
$ sudo setenforce Permissive
# Permanently set in /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted
# Check SELinux context on files
$ ls -Z /var/log/nginx/
system_u:object_r:httpd_log_t:s0 access.log
system_u:object_r:httpd_log_t:s0 error.log
# Check processes
$ ps auxZ | grep nginx
system_u:system_r:httpd_t:s0 nginx: master process /usr/sbin/nginx
# Allow a service to access a directory
$ sudo setsebool -P httpd_read_user_content 1AppArmor (Ubuntu/Debian)
# Check AppArmor status
$ sudo aa-status
apparmor module is loaded.
35 profiles are loaded.
32 profiles are in enforce mode.
3 profiles are in complain mode.
# View a specific profile
$ sudo aa-status --show=nginx
# Set a profile to complain mode (logs violations but allows)
$ sudo aa-complain /usr/sbin/nginx
# Set back to enforce mode
$ sudo aa-enforce /usr/sbin/nginxEssential Security Hardening
1. Disable Root Login Over SSH
# Edit /etc/ssh/sshd_config
$ sudo nano /etc/ssh/sshd_config
PermitRootLogin no
# Reload SSH
$ sudo systemctl reload ssh2. Use Key-Based SSH Only
# In /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes3. Keep the System Updated
# Ubuntu/Debian
$ sudo apt update && sudo apt upgrade -y
# RHEL/CentOS
$ sudo dnf update -y4. Principle of Least Privilege
# Don't run services as root
# Configure web server to run as www-data
$ sudo chown -R www-data:www-data /var/www/html
# Don't give sudo to everyone
$ sudo usermod -aG sudo alice # Only if absolutely needed5. Configure Fail2Ban
# Install fail2ban (blocks brute-force attackers)
$ sudo apt install fail2ban -y
# Configure
$ sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
$ sudo systemctl enable fail2ban
$ sudo systemctl start fail2banQuick Reference
| Task | Command |
|---|---|
| Check UFW status | sudo ufw status verbose |
| Allow SSH | sudo ufw allow ssh |
| Allow port | sudo ufw allow 80/tcp |
| Deny IP | sudo ufw deny from IP |
| Enable firewall | sudo ufw enable |
| List firewalld zones | sudo firewall-cmd --get-active-zones |
| Allow service | sudo firewall-cmd --add-service=http --permanent |
| List iptables rules | sudo iptables -L -n -v |
| Check SELinux | getenforce |
| Check AppArmor | sudo aa-status |
Practice Challenge
- Check if UFW or firewalld is installed:
which ufw firewall-cmd - Run
sudo ufw status verboseif UFW is installed - List all iptables rules:
sudo iptables -L -n - Check your SSH configuration:
grep "^PermitRootLogin" /etc/ssh/sshd_config - View system open ports:
ss -tuln