From 92afe35b0b28fadc0c30b527115c724f9325a784 Mon Sep 17 00:00:00 2001 From: Tore Anderson Date: Sun, 2 Feb 2025 20:05:07 +0100 Subject: [PATCH] Ensure CLAT-PLAT traffic is permitted in UFW UFW is the standard local firewall framework in Debian-based distributions. If it is installed and active, add rules that permit routed traffic between the CLAT and the PLAT prefix. This traffic is dropped by the default UFW ruleset, leading to issues such as #42. --- Makefile | 2 +- README.pod | 12 ++++++++++++ clatd | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 988e5e6..4ed9e8e 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,6 @@ installdeps: # .deb/apt-get based distros if test -x "$(APT_GET)"; then $(APT_GET) -y install perl-base perl-modules libnet-ip-perl libnet-dns-perl libio-socket-ip-perl iproute2 nftables tayga; fi # .rpm/DNF/YUM-based distros - if test -x "$(DNF_OR_YUM)"; then $(DNF_OR_YUM) -y install perl perl-Net-IP perl-Net-DNS perl-IO-Socket-IP perl-File-Temp iproute nftables; fi + if test -x "$(DNF_OR_YUM)"; then $(DNF_OR_YUM) -y install perl perl-IPC-Cmd perl-Net-IP perl-Net-DNS perl-IO-Socket-IP perl-File-Temp iproute nftables; fi # If necessary, try to install the TAYGA .rpm using dnf/yum. It is unfortunately not available in all .rpm based distros (in particular CentOS/RHEL). if test -x "$(DNF_OR_YUM)" && test ! -x "$(TAYGA)"; then $(DNF_OR_YUM) -y install tayga || echo "ERROR: Failed to install TAYGA using dnf/yum, the package is probably not included in your distro. Try enabling the EPEL repo and try again, or install TAYGA directly from source."; exit 1; fi diff --git a/README.pod b/README.pod index c34fff4..410b0f4 100644 --- a/README.pod +++ b/README.pod @@ -262,6 +262,18 @@ L. Required if I is set. Path to the B binary from the TAYGA package available at L. Required. +=item B (default: assume in $PATH) + +Path to the B binary from the UFW local firewall framework available at +L commonly seen on Debian-based distributions. If +this command is present on the system, and B reports that the +firewall is active, B will add firewall rules ensuring traffic between +the CLAT and the PLAT prefix is allowed when it is starting up, and remove them +when it is shutting down. + +If you don't want B to add and remove UFW firewall rules, set this to an +empty string. + =item B (default: I) Controls whether or not B should enable IPv6 forwarding if necessary. diff --git a/clatd b/clatd index 9e6fe53..8ded271 100755 --- a/clatd +++ b/clatd @@ -24,6 +24,7 @@ # clatd(8) for more information. # use strict; +use IPC::Cmd qw(can_run); use Net::IP; my $VERSION = "1.6nat46"; @@ -43,6 +44,7 @@ $CFG{"dns64-servers"} = undef; # use system resolver by default $CFG{"cmd-ip"} = "ip"; # assume in $PATH $CFG{"cmd-nft"} = "nft"; # assume in $PATH $CFG{"cmd-tayga"} = "tayga"; # assume in $PATH +$CFG{"cmd-ufw"} = "ufw"; # assume in $PATH $CFG{"ctmark"} = undef; # match ctmark for routing pkts to CLAT $CFG{"forwarding-enable"} = 1; # enable ipv6 forwarding? $CFG{"plat-dev"} = undef; # PLAT-facing device, default detect @@ -604,6 +606,7 @@ my $cleanup_zero_proxynd_sysctl; # zero proxy_ndp sysctl if set my $cleanup_remove_proxynd_entry, # true if having added proxynd entry my $cleanup_remove_nftable; # true if having added an nftable my $cleanup_remove_clat_iprule; # true if having added clat iprule +my $cleanup_remove_ufw_rules; # true if having added ufw rules my $cleanup_restore_local_iprule_prio; # true if having reordered local iprule my @cleanup_restore_v4_defaultroutes; # temporarily replaced defaultroutes @@ -662,6 +665,14 @@ sub cleanup_and_exit { d("Cleanup: Removing ip rule for redirecting inbound traffic to CLAT"); cmd(\&w, cfg("cmd-ip"), qw(-6 rule del prio 0 table), cfg("route-table")); } + if(defined($cleanup_remove_ufw_rules)) { + cmd(\&w, cfg("cmd-ufw"), qw(route delete allow in on), cfg("clat-dev"), + "from", cfg("clat-v6-addr"), qw(out on), cfg("plat-dev"), "to", + cfg("plat-prefix")); + cmd(\&w, cfg("cmd-ufw"), qw(route delete allow in on), cfg("plat-dev"), + "from", cfg("plat-prefix"), qw(out on), cfg("clat-dev"), "to", + cfg("clat-v6-addr")); + } exit($exitcode); } @@ -885,6 +896,30 @@ if(cfgbool("forwarding-enable")) { } } +# +# Add firewall rules to allow PLAT-CLAT traffic if a supported local firewall +# framework is present and active +# +if(can_run(cfg("cmd-ufw"))) { + open(my $fd, '-|', cfg("cmd-ufw"), "status") + or err("'ufw status' failed to execute"); + while(<$fd>) { + if(/^Status: active$/) { + p("UFW local firewall framework active, allowing CLAT-PLAT traffic"); + cmd(\&err, cfg("cmd-ufw"), qw(route allow in on), cfg("clat-dev"), + "from", cfg("clat-v6-addr"), qw(out on), cfg("plat-dev"), "to", + cfg("plat-prefix")); + cmd(\&err, cfg("cmd-ufw"), qw(route allow in on), cfg("plat-dev"), + "from", cfg("plat-prefix"), qw(out on), cfg("clat-dev"), "to", + cfg("clat-v6-addr")); + $cleanup_remove_ufw_rules = 1; + } + } + close($fd) or err("'ufw status' failed"); +} else { + d("UFW command '", cfg("cmd-ufw"), "' not found, not installing UFW rules"); +} + # # Enable ND proxy for the CLAT's IPv6 address on the interface facing the PLAT #