Skip to content

firewall issues

  • The lowest level on linux seems to be netfilter.
  • upon netfilter there now is nftables, which should be preferred if only because iptables is on the way out.
  • upon that there is iptables (without nft iptables still can use directly on netfilter)
  • upon iptables is ufw, fail2ban can work directly on both iptables and nft

Update: it seems nft is simple enough at least for use to basically block everything except some ports. So ufw will only complicate things (see the extra chains it adds later on).

For for now on most server we aim at nftables and not even fail2ban, since we know all hosts that should be able to access the servers : hoek, and laptop.

So we can device a default ruleset that can even be put in the aivrp repository. Since all it says is what hosts may enter, it is not even that secret.

netfilter

[netfilter page] (https://www.netfilter.org/)

From the netfilter page :

The netfilter hooks are a framework inside the Linux kernel that allows kernel modules to register callback functions at different locations of the Linux network stack. The registered callback function is then called back for every packet that traverses the respective hook within the Linux network stack.

but also :

  • iptables is a generic firewalling software that allows you to define rulesets. Each rule within an IP table consists of a number of classifiers (iptables matches) and one connected action (iptables target).
  • nftables is the successor of iptables, it allows for much more flexible, scalable and performance packet classification. This is where all the fancy new features are developed.

So whenever a packet goes through the network system, a callback is called whenever a 'hook' is registered for that packet.

I presume we don't need to go that deep, so let netfilter rest for now.

iptables

First there was ipchains, then there was iptables, next chapter there is nftables.

iptables seems to be a passed station now, so i will leave it with this... Note that there was a separate ip6tables so another reason to move to nftables.

It seems we can't remove iptables without removing ufw, so to be sure we are not mixing up stuff just flush all chain (F), user-defined chains(X) and counters(Z)

As far as the interweb says, ufw is built as a shell upon iptables (only), but if nftables is installed on the system, iptables detects that and interacts with the nftables-stack so it really is this stack :

ufw
iptables
nftables
netfilter

Maybe we CAN remove ufw + iptables altogether if we know enough, right now ufw is too convenient to remove.

wget -O - v6.ident.me 2>/dev/null  && echo

This will give the current dynamic address, so the outgoing address which will keep changing

    inet6 2001:4c3c:1d01:2300:2097:a9ed:27de:3a20/64 scope global temporary dynamic 

Note that consecutive calls to ip -a will say :

    inet6 2001:4c3c:1d01:2300:2097:a9ed:27de:3a20/64 scope global temporary dynamic 
       valid_lft 6715sec preferred_lft 4015sec
    inet6 2001:4c3c:1d01:2300:255d:1999:99e2:b941/64 scope global temporary deprecated dynamic 
       valid_lft 6715sec preferred_lft 0sec

And a couple of seconds later, the valid_lft will be lower

    inet6 2001:4c3c:1d01:2300:2097:a9ed:27de:3a20/64 scope global temporary dynamic 
       valid_lft 6703sec preferred_lft 4003sec
    inet6 2001:4c3c:1d01:2300:255d:1999:99e2:b941/64 scope global temporary deprecated dynamic 
       valid_lft 6703sec preferred_lft 0sec

When the preferred_lft reaches 0 the address will most likely become deprecated, and a new one will be opened as new global dynamic but the other ones stay active but they won't be used for outgoing connections.

At Sat Jun 21 12:28:27 PM CEST 2025

This was the enp6s0 interface assignment.

2: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 58:11:22:be:c9:9f brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.151/24 brd 192.168.1.255 scope global dynamic noprefixroute enp6s0
       valid_lft 76636sec preferred_lft 76636sec
    inet6 2001:4c3c:1d01:2300:2097:a9ed:27de:3a20/64 scope global temporary dynamic 
       valid_lft 6703sec preferred_lft 4003sec
    inet6 2001:4c3c:1d01:2300:255d:1999:99e2:b941/64 scope global temporary deprecated dynamic 
       valid_lft 6703sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:a2f7:cb56:71ba:25d4/64 scope global temporary deprecated dynamic 
       valid_lft 6703sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:5dd5:c1c1:b033:c8d/64 scope global temporary deprecated dynamic 
       valid_lft 6703sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:166e:7ccb:1cb4:81f/64 scope global temporary deprecated dynamic 
       valid_lft 6703sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:600b:64fb:fa0e:72a6/64 scope global temporary deprecated dynamic 
       valid_lft 6703sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:92c2:75:dd87:a224/64 scope global temporary deprecated dynamic 
       valid_lft 6703sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:5a11:22ff:febe:c99f/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 6703sec preferred_lft 4003sec
    inet6 fe80::5a11:22ff:febe:c99f/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

However this is the listing at : Sat Jun 21 12:47:09 PM CEST 2025 This is later, both valid_lft and preferred_lft have increased !!

2: enp6s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 58:11:22:be:c9:9f brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.151/24 brd 192.168.1.255 scope global dynamic noprefixroute enp6s0
       valid_lft 75205sec preferred_lft 75205sec
    inet6 2001:4c3c:1d01:2300:2097:a9ed:27de:3a20/64 scope global temporary dynamic 
       valid_lft 7163sec preferred_lft 4463sec
    inet6 2001:4c3c:1d01:2300:255d:1999:99e2:b941/64 scope global temporary deprecated dynamic 
       valid_lft 7163sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:a2f7:cb56:71ba:25d4/64 scope global temporary deprecated dynamic 
       valid_lft 7163sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:5dd5:c1c1:b033:c8d/64 scope global temporary deprecated dynamic 
       valid_lft 7163sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:166e:7ccb:1cb4:81f/64 scope global temporary deprecated dynamic 
       valid_lft 7163sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:600b:64fb:fa0e:72a6/64 scope global temporary deprecated dynamic 
       valid_lft 7163sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:92c2:75:dd87:a224/64 scope global temporary deprecated dynamic 
       valid_lft 7163sec preferred_lft 0sec
    inet6 2001:4c3c:1d01:2300:5a11:22ff:febe:c99f/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 7163sec preferred_lft 4463sec
    inet6 fe80::5a11:22ff:febe:c99f/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

Seconds later they drop again, so there is a process that refreshes these counters. It would make sense to review this a day/week/month later to be sure.

# this one at least skips the ipv4 part and local addresses
    ip -6 a show scope global

It seems that all addresses have this prefix.

2001:4c3c:1d01:2300:

That seems to correspond with the /64, because that is half of the 128 bit ipv6 space.

  • 2 = 4 bits (0x0-0xF, 2^4 == 16)
  • 20 = 8 bits
  • 2001 = 16 bits (0x0000-0xFFFF, 2^16 = 65536)
  • 2001:4c3x = 32 bits (2^32 = 4294967296)
  • 2001:4c3x:1d01:2300 = 64 bits

nftables

beginners guide red hat documentation quick ref

The tool is 'nft' a horrible name because google prefers non-fungible-tokens

https://wiki.nftables.org/wiki-nftables/index.php/Main_Page

The command most resembling iptables -L is :

nft list ruleset

strato problems

Strato seems to wipe the ipv6 address when using these rules :

    icmpv6 type { echo-request, nd-neighbor-solicit } accept

But a web search stated:

Ensure you allow essential ICMPv6 messages like router solicitations and neighbor discovery messages for proper IPv6 operation. You can do this by adding rules like add rule inet filter input icmpv6 type { nd-router-solicit, nd-neighbor-solicit, nd-neighbor-advert } accept.

It seems to be both icmp and dhcp that need to be opened.

    icmpv6 type { echo-request, echo-reply, nd-neighbor-solicit, mld-listener-query, nd-router-solicit, nd-neighbor-advert} accept
    udp dport { 546, 547 } accept

546,547 are used for dhcpv6,

you can watch ip a and see the counters go down to about 300, then jump up again to 600. So it looks like ever 5 minutes dhcp triggers a refresh for the address, and if we don't open the dhcp port it will continue and go down to 0, then the address is removed.

Note that we debugged this with the following rules in nftables.

    # open tcp ports: sshd (22), httpd (80)
    tcp dport { ssh, https } accept

    counter comment "total unfilter input packets"
    log flags all
    log flags all prefix "GOTCHA!:"
    log flags all counter
  }

On VNC these lines just appears in the console, when logged in you need journalctl

Fucking AI search didn't help me in ANY WAY to find this :

journalctl -b | grep GOTCHA

We already added the 546 port, but still it dumps packets like these

Jun 21 15:54:01 a01c4504-bbe6-4b09-a151-38fa795c4671 kernel: IN=ens6 OUT= MACSRC=82:01:a4:6c:59:67 MACDST=02:01:a4:6c:59:67 MACPROTO=86dd SRC=2a06:4880:6000:0000:0000:0000:0000:0087 DST=2a01:0239:0253:d100:0000:0000:0000:0001 LEN=64 TC=0 HOPLIMIT=244 FLOWLBL=528820 PROTO=TCP SPT=59531 DPT=9300 SEQ=705941139 ACK=0 WINDOW=14600 RES=0x00 SYN URGP=0 OPT (020405A0) 
Jun 21 15:55:56 a01c4504-bbe6-4b09-a151-38fa795c4671 kernel: IN=ens6 OUT= MACSRC=82:01:a4:6c:59:67 MACDST=02:01:a4:6c:59:67 MACPROTO=86dd SRC=2001:0470:0001:0fb5:0000:0000:0000:01ee DST=2a01:0239:0253:d100:0000:0000:0000:0001 LEN=60 TC=0 HOPLIMIT=237 FLOWLBL=0 PROTO=TCP SPT=33382 DPT=143 SEQ=3652609533 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 
Jun 21 15:55:56 a01c4504-bbe6-4b09-a151-38fa795c4671 kernel: GOTCHA!:IN=ens6 OUT= MACSRC=82:01:a4:6c:59:67 MACDST=02:01:a4:6c:59:67 MACPROTO=86dd SRC=2001:0470:0001:0fb5:0000:0000:0000:01ee DST=2a01:0239:0253:d100:0000:0000:0000:0001 LEN=60 TC=0 HOPLIMIT=237 FLOWLBL=0 PROTO=TCP SPT=33382 DPT=143 SEQ=3652609533 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 
Jun 21 15:55:56 a01c4504-bbe6-4b09-a151-38fa795c4671 kernel: IN=ens6 OUT= MACSRC=82:01:a4:6c:59:67 MACDST=02:01:a4:6c:59:67 MACPROTO=86dd SRC=2001:0470:0001:0fb5:0000:0000:0000:01ee DST=2a01:0239:0253:d100:0000:0000:0000:0001 LEN=60 TC=0 HOPLIMIT=237 FLOWLBL=0 PROTO=TCP SPT=33382 DPT=143 SEQ=3652609533 ACK=0 WINDOW=65535 RES=0x00 SYN URGP=0 
Jun 21 15:57:33 a01c4504-bbe6-4b09-a151-38fa795c4671 kernel: IN=ens6 OUT= MACSRC=82:01:a4:6c:59:67 MACDST=02:01:a4:6c:59:67 MACPROTO=86dd SRC=2a06:4880:6000:0000:0000:0000:0000:0091 DST=2a01:0239:0253:d100:0000:0000:0000:0001 LEN=64 TC=0 HOPLIMIT=245 FLOWLBL=979862 PROTO=TCP SPT=48773 DPT=10070 SEQ=1662033206 ACK=0 WINDOW=14600 RES=0x00 SYN URGP=0 OPT (020405A0) 
Jun 21 15:57:33 a01c4504-bbe6-4b09-a151-38fa795c4671 kernel: GOTCHA!:IN=ens6 OUT= MACSRC=82:01:a4:6c:59:67 MACDST=02:01:a4:6c:59:67 MACPROTO=86dd SRC=2a06:4880:6000:0000:0000:0000:0000:0091 DST=2a01:0239:0253:d100:0000:0000:0000:0001 LEN=64 TC=0 HOPLIMIT=245 FLOWLBL=979862 PROTO=TCP SPT=48773 DPT=10070 SEQ=1662033206 ACK=0 WINDOW=14600 RES=0x00 SYN URGP=0 OPT (020405A0) 

Ports tried :

  • 9300 : seems to be elastic search ??
  • 143 : imap , do we need that ? keep in mind
  • 10070 : unknown
  • 8100 : xprint server ?

It might be worthwhile to look at this dump now and then.

ufw

In short... we don't need ufw anymore !

Ufw does close a lot by default which is a good thing, but it does mean we lose ssh access when fired up. Let's see what rules correspond with opening and closing ssh or another port in nft.

ufw status

This does seem to be more 'uncomplicated'

Status: active

To                         Action      From
--                         ------      ----
Anywhere                   ALLOW       192.168.1.0/24            
7002                       ALLOW       Anywhere                  
Anywhere (v6)              ALLOW       fe80::/64                 
Anywhere (v6)              ALLOW       2001:4c3c:1d01:2300::/64  
7002 (v6)                  ALLOW       Anywhere (v6)             
Anywhere (v6)              ALLOW       2a01:239:253:d100::1      
Anywhere (v6)              ALLOW       2a02:a45e:da02:0:ce4a:7725:368d:90ad/64

Note that this table is maintained in the files /etc/ufw/user[6].rules

*filter
:ufw6-user-input - [0:0]
:ufw6-user-output - [0:0]
:ufw6-user-forward - [0:0]
:ufw6-before-logging-input - [0:0]
:ufw6-before-logging-output - [0:0]
:ufw6-before-logging-forward - [0:0]
:ufw6-user-logging-input - [0:0]
:ufw6-user-logging-output - [0:0]
:ufw6-user-logging-forward - [0:0]
:ufw6-after-logging-input - [0:0]
:ufw6-after-logging-output - [0:0]
:ufw6-after-logging-forward - [0:0]
:ufw6-logging-deny - [0:0]
:ufw6-logging-allow - [0:0]
:ufw6-user-limit - [0:0]
:ufw6-user-limit-accept - [0:0]
### RULES ###

### tuple ### allow any any ::/0 any fe80::/64 in
-A ufw6-user-input -s fe80::/64 -j ACCEPT

### tuple ### allow any any ::/0 any 2001:4c3c:1d01:2300::/64 in
-A ufw6-user-input -s 2001:4c3c:1d01:2300::/64 -j ACCEPT

### tuple ### allow any 7002 ::/0 any ::/0 in
-A ufw6-user-input -p tcp --dport 7002 -j ACCEPT
-A ufw6-user-input -p udp --dport 7002 -j ACCEPT

### tuple ### allow any any ::/0 any 2a01:239:253:d100::1 in
-A ufw6-user-input -s 2a01:239:253:d100::1 -j ACCEPT

### tuple ### allow any any ::/0 any 2a02:a45e:da02:0:ce4a:7725:368d:90ad/64 in
-A ufw6-user-input -s 2a02:a45e:da02:0:ce4a:7725:368d:90ad/64 -j ACCEPT

### END RULES ###

### LOGGING ###
-A ufw6-after-logging-input -j LOG --log-prefix "[UFW BLOCK] " -m limit --limit 3/min --limit-burst 10
-A ufw6-after-logging-forward -j LOG --log-prefix "[UFW BLOCK] " -m limit --limit 3/min --limit-burst 10
-I ufw6-logging-deny -m conntrack --ctstate INVALID -j RETURN -m limit --limit 3/min --limit-burst 10
-A ufw6-logging-deny -j LOG --log-prefix "[UFW BLOCK] " -m limit --limit 3/min --limit-burst 10
-A ufw6-logging-allow -j LOG --log-prefix "[UFW ALLOW] " -m limit --limit 3/min --limit-burst 10
### END LOGGING ###

### RATE LIMITING ###
-A ufw6-user-limit -m limit --limit 3/minute -j LOG --log-prefix "[UFW LIMIT BLOCK] "
-A ufw6-user-limit -j REJECT
-A ufw6-user-limit-accept -j ACCEPT
### END RATE LIMITING ###
COMMIT

You can probably recognize the rules and also see that they are in ip6tables format.

If we try to find them in the nft rulesets

        chain ufw6-user-input {
                ip6 saddr fe80::/64 counter packets 0 bytes 0 accept
                ip6 saddr 2001:4c3c:1d01:2300::/64 counter packets 0 bytes 0 accept
                tcp dport 7002 counter packets 0 bytes 0 accept
                udp dport 7002 counter packets 0 bytes 0 accept
                ip6 saddr 2a01:239:253:d100::1 counter packets 0 bytes 0 accept
                ip6 saddr 2a02:a45e:da02::/64 counter packets 0 bytes 0 accept
        }

As you see these are the exact match for the ipv6 set of ufw, the two other rules are in this ruleset

        chain ufw-user-input {
                ip saddr 192.168.1.0/24 counter packets 8 bytes 4134 accept
                tcp dport 7002 counter packets 0 bytes 0 accept
                udp dport 7002 counter packets 0 bytes 0 accept
        }       

So we need an extra chain to store our rules and jump to them from a higher chain. For the ufw chain we see that :

        table ip filter {
            chain INPUT {
                type filter hook input priority filter; policy drop;
                ...
                counter packets 106727 bytes 47592420 jump ufw-before-input
                ...
            }

            chain ufw-before-input {
                ...
                counter packets 22 bytes 6272 jump ufw-user-input
            }
        }

        # and there is a separate table for ipv6, but with the same path : 
        table ip6 filter {
            chain INPUT {
               counter packets 213508 bytes 239637752 jump ufw6-before-input
            }

            chain ufw6-before-input {
                counter packets 0 bytes 0 jump ufw6-user-input
            }
        }

I suggest to see if we can make a custom chain with ufw and use that to :

  • add a blocking rule from ufw
  • remove blocking rule from software
  • add a blocking rule from software
  • remove blocking rule from ufw

Mixed :

So