Arch installs with very little extra security on top — that's the philosophy. The flip side is that any hardening is something you set up. This guide is the gentle walkthrough: what to do, in what order, on a fresh Arch install you're putting on the internet (or just want to be sane about).
This pairs with the post-install: the real checklist guide — that one covers the basics (sudo user, time, locale, network). This one is the next layer: SSH, firewall, intrusion protection, kernel knobs, AppArmor.
The big picture
- SSH: keys only, no root login, restricted users.
- A firewall (nftables) that blocks by default.
- fail2ban (or sshguard) to discourage brute force.
- A handful of kernel sysctls flipped.
- AppArmor or SELinux turned on.
- Automatic updates — or at least a habit of running them often.
Step 1: SSH the same as everywhere
If you've done this on Debian or Mint, the process is identical. From your laptop:
ssh-keygen -t ed25519 # if you don't have one
ssh-copy-id alice@server
Test logging in with the key (no password). Then on the server:
sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
AllowUsers alice
sudo systemctl reload sshd
Keep your existing SSH session open until you've confirmed a fresh one still works.
Step 2: a firewall with nftables
nftables is part of the kernel itself — no separate install needed beyond the userland tool:
sudo pacman -S nftables
A minimal, sane ruleset:
sudo nano /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established,related accept
ct state invalid drop
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
tcp dport 22 accept
# uncomment for a web server:
# tcp dport { 80, 443 } accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Check the syntax before activating (this never lets a broken file lock you out):
sudo nft -c -f /etc/nftables.conf
Enable for boot and start now:
sudo systemctl enable --now nftables
Verify with sudo nft list ruleset — should echo your rules. (See nftables on Trixie for the line-by-line walkthrough; same syntax on Arch.)
Step 3: brute-force protection
The Arch-friendly pick is sshguard — small, no extra Python, works with nftables out of the box:
sudo pacman -S sshguard
sudo systemctl enable --now sshguard
That's it. sshguard parses the journal, and after a few failed SSH attempts from one IP it adds a temporary nftables block. Defaults are reasonable.
To verify it's wired up:
sudo nft list set inet sshguard attackers # the set sshguard fills with banished IPs
journalctl -u sshguard -n 20
Prefer fail2ban? Same idea, more Python and more knobs: sudo pacman -S fail2ban, enable the service.
Step 4: kernel hardening sysctls
Drop this file:
sudo nano /etc/sysctl.d/99-hardening.conf
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv6.conf.all.accept_ra = 0 # only set on servers with static routes
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2
kernel.yama.ptrace_scope = 1
kernel.unprivileged_bpf_disabled = 1
Apply:
sudo sysctl --system
What each is doing (very briefly): no smurf amplification, no source routing, log spoofed source addresses, reject impossible source routes, no IPv6 router advertisements (server use), hide kernel pointers and dmesg from normal users, restrict process tracing, and disable unprivileged BPF.
Step 5: AppArmor
Arch doesn't enable a mandatory access control system by default. AppArmor is the simpler of the two (the other is SELinux). To turn it on:
sudo pacman -S apparmor
Edit your bootloader to add a kernel parameter:
If you use systemd-boot: edit /boot/loader/entries/arch.conf, add to the options line:
options ... lsm=landlock,lockdown,yama,integrity,apparmor,bpf
If you use GRUB: edit /etc/default/grub, append to GRUB_CMDLINE_LINUX_DEFAULT:
GRUB_CMDLINE_LINUX_DEFAULT="... lsm=landlock,lockdown,yama,integrity,apparmor,bpf"
Then sudo grub-mkconfig -o /boot/grub/grub.cfg.
Enable the service and reboot:
sudo systemctl enable apparmor
sudo reboot
After reboot:
sudo aa-status
Should report apparmor loaded and list any active profiles. Profiles for common daemons (sshd, nginx, dovecot) live under /etc/apparmor.d/. Many are in "complain" mode by default — they log violations without enforcing. Switch one to enforce:
sudo aa-enforce /etc/apparmor.d/usr.sbin.sshd
Step 6: automatic-ish updates
Arch doesn't encourage hands-off updates the way Debian does — partial upgrades break Arch in interesting ways. But you can automate the safer half: keep an eye on news and pending updates.
From the AUR: informant is a wrapper that refuses to upgrade until you've acknowledged any new Arch news:
yay -S informant
informant check && sudo pacman -Syu
Schedule a daily check that emails you if updates are pending — not auto-applied, just a nudge. (See cron and systemd timers for the timer pattern.)
Step 7: a few small extras
- Limit who can use sudo. In
/etc/sudoers, the%wheelgroup is your sudoers list. Don't add other groups; don't loosen requirements. - Set a password timeout for sudo. Default is 5 minutes (the duration sudo "remembers" your password). Lower it with
Defaults timestamp_timeout=1. - Disable unused services.
systemctl list-units --type=service --state=running— anything you don't recognise, look up, then disable if unneeded. - If on a laptop: enable full-disk encryption at install time. After install, see changing the LUKS passphrase for ongoing management.
The done check
sudo nft list ruleset # rules look right?
systemctl is-active sshguard nftables apparmor # all "active"?
sudo aa-status | head # apparmor loaded?
sudo sysctl -a 2>/dev/null | grep dmesg_restrict # = 1?
sudo ss -tulpn # nothing surprising listening?
Five commands. If they all look reasonable, your Arch install is in a quietly defensible state.