Write a minimal stateful host firewall that allows SSH from a management subnet and blocks everything else inbound.
Show the answer
Say this first
Default drop on input, then in order: allow loopback, allow established and related traffic, drop invalid, allow ICMP echo, allow TCP 22 only from the management subnet. Outbound stays open. The established rule near the top lets replies to the host's own connections back in and makes the rule set cheap, because most packets match it first. ICMP errors for existing flows arrive as RELATED, so PMTUD keeps working.
Go deeper
iptables
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p tcp -s 10.0.50.0/24 --dport 22 -j ACCEPT
iptables -P INPUT DROPThe policy is set last so an SSH session building the rules is never cut off halfway.
nftables, same logic, IPv4 and IPv6 together
#!/usr/sbin/nft -f
flush ruleset
table inet host {
chain input {
type filter hook input priority filter; policy drop;
iif "lo" accept
ct state established,related accept
ct state invalid drop
meta l4proto { icmp, ipv6-icmp } accept
ip saddr 10.0.50.0/24 tcp dport 22 accept
}
}Saved as /etc/nftables.conf (Debian, Ubuntu) or /etc/sysconfig/nftables.conf (RHEL), loaded at boot by
nftables.service.
Why ICMPv6 must be open
IPv6 neighbour discovery and router advertisements are ICMPv6. Drop them and IPv6 stops working entirely, not just ping.
They will ask next
Why put the established rule before the SSH rule?
Order is performance and safety: almost every packet belongs to an existing flow, so it matches in one step. Only new connections go further down the list.
The host uses Docker. Does this ruleset protect the containers?
No. Published container ports are DNATed and go through FORWARD, not INPUT. Rules for them go in the
DOCKER-USERchain. Also,flush rulesetin the nft file would wipe Docker's rules on reload.
Traps
- Dropping all ICMP. IPv4 PMTUD and the whole of IPv6 depend on it.
- Forgetting loopback. Local services talking to each other on 127.0.0.1 break in odd ways.