Package: ufw
Version: 0.36.2-9
Severity: normal
Tags: upstream

Hi,
I've set up ufw and fail2ban on a machine of mine, but I was wondering: even 
when using REJECT instead of DENY, how will folks trying to reach my machine 
know they've been blocked, as opposed to a connection failing just for any old 
reason? There are ICMP error types specifically for this:
(ICMPv4) Type 3, Destination Unreachable—Code 13, Communication 
Administratively Prohibited 
https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#table-icmp-parameters-codes-3
(ICMPv6) Type 1, Destination Unreachable—Code 1, communication with destination 
administratively prohibited 
https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#table-icmpv6-parameters-codes-2

The iptables-extensions(8) manual page suggests these can be specified if 
iptables is invoked with --reject-with icmp-admin-prohibited or 
icmp6-adm-prohibited, respectively. The nftables wiki has a pertinent page at 
https://wiki.nftables.org/wiki-nftables/index.php/Rejecting_traffic about doing 
this there.

A packet capture with Wireshark shows that, when attempting to connect to an 
IPv6 TCP port on a remote machine which has a service running, but which is 
configured to reject connection attempts, my client instead gets "Destination 
unreachable: Port unreachable". This seems to violate pertinent Internet 
standards that the most appropriate ICMP error type be used so a client can act 
appropriately.

To be honest, I don't understand ufw's code well enough to point to where this 
should be fixed. On a possibly-related note, however, I'm concerned about these 
lines in the source, which would seem to imply a TCP reset (RST) is sent for 
rejected TCP connection attempts:
https://sources.debian.org/src/ufw/0.36.2-9/src/common.py#L158
> def format_rule(self):
>       '''Format rule for later parsing'''
> ⋮
>       elif self.action == "
>               rule_str += " -j REJECT%s" % (lstr)
>               if self.protocol == "tcp":
>                       # follow TCP's default and send RST
>                       rule_str += " --reject-with tcp-reset"

and likewise at 
https://sources.debian.org/src/ufw/0.36.2-9/src/backend_iptables.py#L610

See RFC 3360, "Inappropriate TCP Resets Considered Harmful", which addresses 
firewalls that do TCP resets for a particular reason but the conclusion is more 
general: artificially-contrived TCP resets are inappropriate for firewalls and 
there are better ways to handle the problem. I think in my case, that TCP RST 
code didn't come into effect owing to the connection being rejected as my 
default policy.

Sending correct and useful error information would not only help me with 
troubleshooting and help tell honest users that a roadblock has been 
(accidentally) imposed, but could even help spammers and network abusers know 
that "I'm onto them", as when they are refused by fail2ban, and they need to 
move on. For clients blocked by a firewall, sending them the standard 
indication that they were blocked by a firewall seems like the right thing to 
do, but it doesn't seem to be attainable with ufw right now.

-- System Information:
Debian Release: 13.4
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 
'proposed-updates'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 6.12.85+deb13-amd64 (SMP w/2 CPU threads; PREEMPT)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to