A server without a firewall is an easy target. This episode covers nftables as the modern firewall, iptables for compatibility, UFW as an easy-to-use frontend, fail2ban for brute-force attacks, and strategies for limiting ports exposed to the public.

Now that your LEMP server is running, it's time to lock it down. Episode 13 covers firewalls on Artix: nftables as the modern successor to iptables, iptables for compatibility, UFW as a friendly frontend, and fail2ban to block brute-force attempts.
On Artix, the firewall also runs as an init service. An important difference: nftables and iptables can conflict if enabled together. We'll pick one approach, write its rules, and make sure the rules survive a reboot.
nftables replaces iptables with a more uniform language. Configuration is defined in tables, which contain chains, which contain rules. Rules are written in /etc/nftables.conf and loaded by the nftables service.
Open SSH and web ports with the following configuration:
table inet filter {
chain input {
type filter hook input priority filter;
policy drop;
ct state established,related accept
iif "lo" accept
tcp dport { 22, 80, 443 } accept
}
}policy drop means all incoming traffic is rejected unless explicitly allowed. The ct state established,related accept line permits responses from established connections.
After writing the rules, test then enable the service:
sudo nft -c -f /etc/nftables.conf
sudo rc-update add nftables default
sudo rc-service nftables start
sudo nft list rulesetnft -c -f /etc/nftables.conf checks syntax without applying. The last line shows all active rules — a verification habit you should always practice.
Some legacy scripts and applications still assume iptables. Modern kernels' nftables provides iptables-nft, a compatibility backend that runs iptables commands on top of the nftables engine. That means you can use the old syntax without two separate stacks.
Equivalent example for opening ports:
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -P INPUT DROPThe iptables -A INPUT command adds a rule to the INPUT chain. The default policy is changed to DROP as the last line.
iptables rules don't survive a reboot automatically. Save them with iptables-save and load them at boot:
sudo iptables-save > /etc/iptables/iptables.rules
sudo rc-update add iptables defaultThe iptables service on OpenRC loads /etc/iptables/iptables.rules at start. Make sure the file is saved before rebooting, or the rules are lost.
The Uncomplicated Firewall simplifies management through intuitive commands. Behind the scenes, UFW uses iptables. It's the right choice if you want firewall rules without writing many lines.
Enable and open ports with UFW:
sudo pacman -S ufw ufw-openrc
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80,443/tcp
sudo ufw enable
sudo rc-service ufw statusufw default deny incoming sets a deny-all policy for incoming traffic. The ufw allow commands open the ports you need, one at a time.
Verify the status and active rules:
sudo ufw status verboseThe output of ufw status verbose shows the default policies and the list of open ports. If you like, check the rejection log at /var/log/ufw.log for suspicious activity.
Fail2ban watches logs and blocks IPs making repeated login attempts. Install it along with its service:
sudo pacman -S fail2ban fail2ban-openrcEnable a basic jail for SSH by creating /etc/fail2ban/jail.local:
[sshd]
enabled = true
port = ssh
maxretry = 5
bantime = 1hAfter writing the file, restart fail2ban and check its status:
sudo rc-service fail2ban restart
sudo fail2ban-client status sshdThe output of fail2ban-client status sshd shows the number of blocked IPs. Note that fail2ban uses nftables or iptables as its ban backend.
The first rule of firewalls: only open the ports you genuinely need. For the LEMP server from episode 12, the minimal list is 22 for SSH, 80 and 443 for web. All other ports are denied by default.
For databases used only locally, don't open port 3306 to the public at all. For admin services, consider restricting the source IP:
sudo ufw allow from 203.0.113.10 to any port 8080 proto tcpThe command ufw allow from 203.0.113.10 only lets your office IP access the admin port. This drastically shrinks the attack surface.
Once everything is set up, verify from an external perspective. If nmap isn't installed, add it and scan your machine:
sudo pacman -S nmap
nmap -p- 203.0.113.50The output of nmap -p- 203.0.113.50 shows all open ports on the target machine. Compare it with the list that should be open — if anything looks suspicious, check the service behind it.
Episode 13 locked down your server: nftables for a modern firewall, iptables for compatibility, UFW as a frontend, fail2ban to block brute-force attempts, and strategies to expose only the ports you need.
Key takeaways:
/etc/nftables.conf.iptables-save so they survive reboot.In the next episode, episode 14, we'll cover security and hardening — sudo/doas policy, SSH hardening with key-only auth, firewall verification, routine updates, and auditing with lynis and rkhunter.