0

I'm trying to replace an existing tunnel with firewall rules:

firewall-cmd --zone=public --add-forward-port=port=9999:proto=tcp:toport=9999:toaddr=100.1.1.1

This should forward all incoming TCP connections to 100.1.1.1

The problem is that it does not work (the port stays closed). I'm trying to understand what I'm doing wrong, and the only thing I can think of is that the destination IP address is on a different NIC and is created by Tailscale (similar to a Wireguard VPN), so it is kind of a virtual IP.

So are there restrictions to which IP's you can forward? And is there a way to circumvent them?

external (active)
  target: default
  icmp-block-inversion: no
  interfaces: tailscale0
  sources:
  services: ssh
  ports:
  protocols:
  forward: yes
  masquerade: yes
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources:
  services: dhcpv6-client ssh
  ports: 9999/tcp
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
port=9999:proto=tcp:toport=9999:toaddr=100.1.1.1
  source-ports:
  icmp-blocks:
  rich rules
Maestro
  • 255
  • 1
  • 2
  • 8
  • Virtual IPs shouldn't be an issue -- are you sure that 1) connections aren't being blocked before they get your firewalld host, and 2) they aren't being blocked by 100.1.1.1 itself? You may want to try using tcpdump on your firewalld host to see what's coming in and going out. – Justin Ludwig Jun 07 '22 at 21:34
  • @JustinLudwig I forwarded the port with the socat utility, it works fine. With reverse ssh tunnel, also fine. With nginx stream proxy, no problem. So I am sure the traffic can reach the host. Only when I try it with firewalld, it does not work. The previous methods all open a listening socket and traffic will appear to come from the host. While with firewall this will not be the case. So I assume the problem has something to do with that. – Maestro Jun 08 '22 at 02:38

1 Answers1

1

You probably need to add masquerading to your port forwarding (to apply SNAT along with the DNAT port-forward, so that 100.1.1.1 knows to respond back through your firewalld host).

The simplest way to do that is to add the outgoing interface for your port forward (ie the tailscale interface, eg tun0) to the external zone:

firewall-cmd --zone=external --add-interface=tun0

Firewalld's external zone comes with masquerading enabled by default. If you're using a custom zone for your tailscale interface, add masquerading to it:

firewall-cmd --zone=myzone --add-masquerade
firewall-cmd --zone=myzone --add-interface=tun0

And of course, make sure you've added the incoming interface for your port forward (ie probably some physical interface, eg eth0) to the public zone (the same zone in which you set up the port forward):

firewall-cmd --zone=public --add-interface=eth0
Justin Ludwig
  • 1,006
  • 7
  • 8
  • Thank you very much!! Are you sure that is safe to add Tailscale to the external zone? I'm a bit afraid about the implications because "external" sounds like it will be easier for attackers to reach this private network. – Maestro Jun 10 '22 at 00:17
  • I tried what you suggested, but it still does not work :( I added my configuration above, does it look correct? – Maestro Jun 10 '22 at 01:55
  • Your configuration looks correct. You might test it out while running `sudo tcpdump -niany tcp port 9999` on the firewalld host to verify that packets are coming in to its `enp0s3` IP address and being forwarded out to `100.1.1.1`. – Justin Ludwig Jun 10 '22 at 17:27
  • Okay, I finally figured it out. I feel very stupid, but there was a process listening on the port I was trying to forward. It was binded by docker, even though the container did not listen to it, docker still did, and dropped all the packets. After removing the bindings in docker, I could succesfully forward the port using your steps. Thank you very much. – Maestro Jun 11 '22 at 16:12