Podman is no longer supporting iptables so I am trying to learn how to set up nftables in its place. It’s been a struggle to get it to work properly. I can not ping my own server after starting the nftables rules. I am using Alpine Linux v2.24.1 and nftables v1.1.6 (Commodore Bullmoose #7).
nftables has a config file with basic rules which include receiving pings:
/etc/nftables.nft
#!/usr/sbin/nft -f
# vim: set ts=4 sw=4:
# You can find examples in /usr/share/nftables/.
# Clear all prior state
flush ruleset
# Basic IPv4/IPv6 stateful firewall for server/workstation.
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iifname lo accept \
comment "Accept any localhost traffic"
ct state { established, related } accept \
comment "Accept traffic originated from us"
ct state invalid drop \
comment "Drop invalid connections"
tcp dport 113 reject with icmpx type port-unreachable \
comment "Reject AUTH to make it fail fast"
# ICMPv4
ip protocol icmp icmp type {
echo-reply, # type 0
destination-unreachable, # type 3
echo-request, # type 8
time-exceeded, # type 11
parameter-problem, # type 12
} accept \
comment "Accept ICMP"
# ICMPv6
icmpv6 type {
destination-unreachable, # type 1
packet-too-big, # type 2
time-exceeded, # type 3
parameter-problem, # type 4
echo-request, # type 128
echo-reply, # type 129
} accept \
comment "Accept basic IPv6 functionality"
icmpv6 type {
nd-router-solicit, # type 133
nd-router-advert, # type 134
nd-neighbor-solicit, # type 135
nd-neighbor-advert, # type 136
} ip6 hoplimit 255 accept \
comment "Allow IPv6 SLAAC"
icmpv6 type {
mld-listener-query, # type 130
mld-listener-report, # type 131
mld-listener-reduction, # type 132
mld2-listener-report, # type 143
} ip6 saddr fe80::/10 accept \
comment "Allow IPv6 multicast listener discovery on link-local"
ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept \
comment "Accept DHCPv6 replies from IPv6 link-local addresses"
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
# The state of stateful objects saved on the nftables service stop.
include "/var/lib/nftables/*.nft"
# Rules
include "/etc/nftables.d/*.nft"
I also have a small config file:
/etc/nftables.d/firewall.nft
#!/usr/sbin/nft -f
define WIREGUARD_PORT = 51820
define WIREGUARD_ADDRESS = 10.0.0.0/24
define SSH_PORT = 5025
define SSH_ADDRESSES = { $WIREGUARD_ADDRESS . $SSH_PORT, 192.168.40.204 . $SSH_PORT }
define PUBLIC_PORTS = { 5050 }
table inet filter {
chain input {
udp dport $WIREGUARD_PORT accept \
comment "Accept WireGuard connections"
ip saddr . tcp dport $SSH_ADDRESSES accept \
comment "Accept SSH connections from known devices or WireGuard"
tcp dport $PUBLIC_PORTS accept \
comment "Accept public connections"
}
}
After loading the new rules, I get the following output while listing the ruleset:
21:23 server-pi:~ $ doas nft list ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
iifname "lo" accept comment "Accept any localhost traffic"
ct state { established, related } accept comment "Accept traffic originated from us"
ct state invalid drop comment "Drop invalid connections"
tcp dport 113 reject comment "Reject AUTH to make it fail fast"
ip protocol icmp icmp type { echo-reply, destination-unreachable, echo-request, time-exceeded, parameter-problem } accept comment "Accept ICMP"
icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply } accept comment "Accept basic IPv6 functionality"
icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept comment "Allow IPv6 SLAAC"
icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-done, mld2-listener-report } ip6 saddr fe80::/10 accept comment "Allow IPv6 multicast listener discovery on link-local"
ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept comment "Accept DHCPv6 replies from IPv6 link-local addresses"
udp dport 51820 accept comment "Accept WireGuard connections"
ip saddr . tcp dport { 10.0.0.0/24 . 5025, 192.168.40.204 . 5025 } accept comment "Accept SSH connections from known devices or WireGuard"
tcp dport 5050 accept comment "Accept public connections"
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
21:23 server-pi:~ $ doas netstat -tunlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN 3515/rootlessport
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 3584/rootlessport
tcp 0 0 0.0.0.0:5025 0.0.0.0:* LISTEN 3743/sshd: /usr/sbi
tcp6 0 0 :::5025 :::* LISTEN 3743/sshd: /usr/sbi
tcp6 0 0 :::5050 :::* LISTEN 3515/rootlessport
udp 0 0 0.0.0.0:51820 0.0.0.0:* -
udp6 0 0 :::51820 :::* -
21:23 server-pi:~ $
I can connect perfectly fine with SSH, WireGuard and my reverse proxy on port 5050 but if I ping the server I don’t get any response at all. Pings worked as normal when I was using iptables so I am not sure what I am doing wrong with nftables. I’ve tried to keep the rules as simple as possible to figure out what is happening but I have not been able to make any progress. Any help would be appreciated.
nftables offers a very decent debugging interface. First, you add a rule to trace the packet (a new chain with high priority works best). Usually I’d suggest to add those rules by hand instead of relying on declarative configs:
nft add table ip ping_trace nft 'add chain ip ping_trace prerouting { type filter hook prerouting priority -301; policy accept; }'Then you add the actual tracing rule there with the shape to match the incoming traffic:
nft 'add rule ip ping_trace prerouting icmp type { echo-request, echo-reply } meta nftrace set 1'now you can run
nft monitor traceand see the decisions made for the matching packets. Remember to delete the ping_trace table afterwards to clean up.I tried what you said. I sent a ping from my computer to the server and this was the output of
nft monitor trace:trace id 1d01c81e ip ping_trace prerouting packet: iif "eth0" ether saddr b0:7d:64:e8:8f:3c ether daddr d8:3a:dd:de:28:99 ip saddr 192.168.40.201 ip daddr 192.168.40.203 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 65074 ip length 84 icmp type echo-request icmp code 0 icmp id 35586 icmp sequence 0 trace id 1d01c81e ip ping_trace prerouting rule icmp type { echo-reply, echo-request } meta nftrace set 1 (verdict continue) trace id 1d01c81e ip ping_trace prerouting policy accept trace id 1d01c81e inet filter input conntrack: ct direction original ct state new ct id 271120081 trace id 1d01c81e inet filter input packet: iif "eth0" ether saddr b0:7d:64:e8:8f:3c ether daddr d8:3a:dd:de:28:99 ip saddr 192.168.40.201 ip daddr 192.168.40.203 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 65074 ip protocol icmp ip length 84 icmp type echo-request icmp code 0 icmp id 35586 icmp sequence 0 trace id 1d01c81e inet filter input rule ip protocol icmp icmp type { echo-reply, destination-unreachable, echo-request, time-exceeded, parameter-problem } accept comment "Accept ICMP" (verdict accept)I sort of get what’s happening and it looks like the ping request has been accepted.
From my computer when I send a ping it shows:
15:55 dell:/tmp/ $ ping -c1 192.168.40.203 PING 192.168.40.203 (192.168.40.203): 56 data bytes --- 192.168.40.203 ping statistics --- 1 packets transmitted, 0 packets received, 100% packet lossSo even though it’s being accepted, I still get nothing going back to my computer, at least that’s how I understand it.
Have you tried both
ping4andping6to isolate whether it is ipv4 or ipv6 or both that’s filtering icmp? Also, can you ping other hosts from your server (ie. does the echo-reply get dropped at the input?)You could also add logging when a packet is rejected so you can see it in your syslog. Put this at the end of your input chain:
log level warn prefix "Input Packet Dropped: "I didn’t think to try ping6. It looks like I can reliably get responses from ping6 but not ping4
The server can ping other devices on the same network just fine
Also, I’m glad you mentioned
syslog. I couldn’t figure out logging and it turns out I had to add the syslog package to my server to get logging working
My server has this to allow ICMP in nftables:
ip protocol icmp accept meta l4proto ipv6-icmp acceptYes, it just allows all of it. You could use it as a starting point (if it works) then fine-tune it. I have not tuned mine at all.
I don’t see the keyword “ipv6-icmp” in your config. It might have something to do with that.
There’s a hundred ways to do the same thing, and I haven’t found online guides to be consistent.
I tried your suggested rules and still nothing
I went a step further and simply enabled all incoming connections with:
table inet filter { chain input { type filter hook input priority 0; policy allow; } }Again I can connect with SSH and WireGuard but I still can’t ping my server. If I restore to my last backup with iptables, I can get a response from ping again.
I also tried directly translating the rules from iptables with:
iptables-save > /tmp/iptables.dump iptables-restore-translate -f /tmp/iptables.dump > nftables.dumpand adding the rules:
#!/usr/sbin/nft -f define WIREGUARD_PORT = 51820 define WIREGUARD_ADDRESS = 10.0.0.0/24 define SSH_PORT = 5025 define SSH_ADDRESSES = { $WIREGUARD_ADDRESS . $SSH_PORT, 192.168.40.204 . $SSH_PORT } define PUBLIC_PORTS = { 5050 } table inet filter { chain input { udp dport $WIREGUARD_PORT accept \ comment "Accept WireGuard connections" ip saddr . tcp dport $SSH_ADDRESSES accept \ comment "Accept SSH connections from known devices or WireGuard" tcp dport $PUBLIC_PORTS accept \ comment "Accept public connections" icmp type echo-request limit rate 5/second burst 10 packets counter accept icmp type echo-request limit rate 30/minute burst 120 packets counter accept icmp type echo-request limit rate 1/minute burst 2 packets counter log prefix " PING-PONG-FLOOD " icmp type echo-request counter drop icmp type destination-unreachable counter accept icmp type time-exceeded counter accept icmp type parameter-problem counter accept icmp type echo-request counter accept } chain forward { icmp type destination-unreachable counter accept icmp type time-exceeded counter accept icmp type parameter-problem counter accept icmp type echo-request counter accept } }and still no ping from my server…
I will agree, the documentation for nftables is just not as accessible or consistent as iptables. It’s a bit frustrating.
Run
nft list rulesetand make sure that the final result matches your config. Maybe it’s misinterpreting or discarding something?
Windscribe

