Let's hope someone finds this answer useful, as I did not find these things online any where!
I received the "Access Denied" error because when I first stated in my question that I had
http_port 3128 intercept
It meant that this squid port was dedicated to intercepting the traffic rather than forwarding it, hence firewalld was expected to forward the traffic from port 3128 to another port, for example port 3126:
firewall-cmd --permanent --zone=internal --add-forward-port=port=80:proto=tcp:toport=3126:toaddr=LAN_INTERFACE_IP
firewall-cmd --reload
to enable squid processing the traffic and forward it to port 3126, while listening to port 3128. Off course, I had to open port 3126 as follows:
firewall-cmd --permanent --zone=internal --add-port=3126/tcp
and I can do squid this way then:
http_port 3126 intercept
http_port 3128 ssl-bump generate-host-certificates=on dynamic_cert_mem_cache_size=4MB cert=/etc/squid/ssl_cert/myca.pem
Note that port 3128 is not in intercept mode. So, when firewalld forwards http traffic to port 3126. squid does its job listening to port 3128 while ipv4 forwarding forwards unknown traffic (which is what we wanted in the first place) to WAN interface i.e. ens33.
To be able to reach the server from a different subnet, simply add a static route on LAN interface where the gateway of this route is set to the actual gateway of the subnet to which LAN interface IP belongs.
Concerning the certificate thing, I did the following:
mkdir /etc/squid/ssl_cert/
cd /etc/squid/ssl_cert
openssl req -new -newkey rsa:1024 -days 1365 -nodes -x509 -keyout myca.pem -out myca.pem
openssl x509 -in myca.pem -outform DER -out myca.der
chown -R squid:squid /etc/squid/ssl_cert/
Finally, SELinux is enabled, I solved this way, ironically I found the solution in the error log of SELinux:
grep ssl_crtd /var/log/audit/audit.log | audit2allow -M ssl_crtd_pol
semodule -i ssl_crtd_pol
This should answer my question and all of my comments above..
Reason it's not working
It appears firewalld might be geared to handle firewalling local services, rather than routed services.
So the tftp settings will add in the end these nft rules when firewalld has been configured (on CentOS 8) with the zones files in OP (just showing the rules, not the whole ruleset here):
table inet firewalld {
chain filter_IN_external_allow {
udp dport 69 ct helper set "helper-tftp-udp"
}
chain filter_IN_internal_allow {
udp dport 69 ct helper set "helper-tftp-udp"
udp dport 69 ct state { new, untracked } accept
}
}
Those rules will never match and are thus useless: they are in the input path, not in the forward path.
With the running firewall, these (blindly copied) rules added at the right place: in the forward path, will make TFTP work:
nft insert rule inet firewalld filter_FWDI_internal_allow udp dport 69 ct helper set "helper-tftp-udp"
nft add rule inet firewalld filter_FWDI_internal_allow index 0 udp dport 69 ct state '{ new, untracked }' accept
So in the end a so-called direct option would still be an option so everything is stored in firewalld's configuration. Alas the documentation is a bit misleading:
Warning: Direct rules behavior is different depending on the value of
FirewallBackend. See CAVEATS in firewalld.direct(5).
Not reading carefully one would think with FirewallBackend=nftables
that it would behave differently by accepting nftables rules, but that's not the case:
# firewall-cmd --version
0.8.0
# firewall-cmd --direct --add-rule inet firewalld filter_FWDI_internal_allow 0 'udp dport 69 ct helper set "helper-tftp-udp" ct state new accept'
Error: INVALID_IPV: invalid argument: inet (choose from 'ipv4', 'ipv6', 'eb')
No need to test much more, this "feature" is documented there:
https://bugzilla.redhat.com/show_bug.cgi?id=1692964
and there:
https://github.com/firewalld/firewalld/issues/555
Direct rules still use iptables with the nftables backend. The CAVEAT is about the order of rules evaluation.
Handle this in an other table
I don't see the point anymore of doing this with firewall-cmd, which will add iptables rules along nftables rules. It just becomes cleaner to add an independent table. It'll just be in the ip family since filters for the specific IPv4 networks will also be added (inet would also be fine).
handletftp.nft
(to be loaded with nft -f handletftp.nft
):
table ip handletftp
delete table ip handletftp
table ip handletftp {
ct helper helper-tftp {
type "tftp" protocol udp
}
chain sethelper {
type filter hook forward priority 0; policy accept;
ip saddr 192.168.1.0/24 ip daddr 10.0.10.10 udp dport 69 ct helper set "helper-tftp"
}
}
As the table is different and the ruleset is never flushed, but instead the specific table is (atomically) deleted and recreated, this doesn't affect firewalld nor firewalld will affect it.
The priority doesn't matter much: that this chain is traversed before or after firewalld's chains won't change the fate of the packet (still in the hands of firewalld). Whatever the order, if the packet is accepted by firewalld it will also have activated the helper for this flow.
If you choose to use the nftables service to load this table, you'll have to edit it (eg: systemctl edit --full nftables
), because beside loading some probably inadequate default rules, it will flush all rules on stop or reload, disrupting firewalld.
Now, a TFTP transfer will work and activate the specific helper, as can be checked by running two conntrack
commands during the transfer:
# conntrack -E & conntrack -E expect
[1] 3635
[NEW] 300 proto=17 src=10.0.10.10 dst=10.0.10.11 sport=0 dport=56597 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.2 master-dst=10.0.10.10 sport=56597 dport=69 class=0 helper=tftp
[NEW] udp 17 29 src=192.168.1.2 dst=10.0.10.10 sport=56597 dport=69 [UNREPLIED] src=10.0.10.10 dst=10.0.10.11 sport=69 dport=56597 helper=tftp
[DESTROY] 299 proto=17 src=10.0.10.10 dst=10.0.10.11 sport=0 dport=56597 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=192.168.1.2 master-dst=10.0.10.10 sport=56597 dport=69 class=0 helper=tftp
[NEW] udp 17 30 src=10.0.10.10 dst=10.0.10.11 sport=42032 dport=56597 [UNREPLIED] src=192.168.1.2 dst=10.0.10.10 sport=56597 dport=42032
[UPDATE] udp 17 30 src=10.0.10.10 dst=10.0.10.11 sport=42032 dport=56597 src=192.168.1.2 dst=10.0.10.10 sport=56597 dport=42032
The 3rd NEW entry in the example above is actually tagged as RELATED (that's the whole role of the tftp helper: expect a certain type of packet to get it seen as related) which will be accepted by the firewall.
Best Answer
This is how it is done:
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 -p tcp -o lo --dport 8080 -j REDIRECT --to-ports 80