Linux – ssh port forwarding with firewall-cmd

firewalldlinuxport-forwardingssh-tunnel

I'm trying to do an ssh tunnel into a server behind NAT:

ssh from laptop –> Host with port forwarding in firewall –> Get directly into guest (172.16.0.2, behind host NAT).

Using iptables on Host – it will work:

# iptables -I OUTPUT  -d 0.0.0.0/0 -j ACCEPT
# iptables -I FORWARD  -d 0.0.0.0/0 -j ACCEPT
# iptables -I INPUT  -d 0.0.0.0/0 -j ACCEPT
# iptables -t nat -I PREROUTING -d 0.0.0.0/0 -p tcp --dport 222 -j DNAT --to-destination 172.16.0.2:22

However, iptables are not saved on Host reboot, since firewalld service is running (firewalld is the default in RHEL 7).

So I'm trying to do the same port forwarding with firewall-cmd.

Using firewall-cmd on Host – it will NOT work:

# firewall-cmd --permanent --zone=public --add-forward-port=port=222:proto=tcp:toport=22:toaddr=172.16.0.2'
# firewall-cmd --permanent --zone=public --add-masquerade
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -d 0.0.0.0/0 -j ACCEPT
# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -d 0.0.0.0/0 -j ACCEPT
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -d 0.0.0.0/0 -j ACCEPT

# firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 0.0.0.0/0 -p tcp --dport 222 -j DNAT --to-destination 172.16.0.2:22

# firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" forward-port port="222" protocol="tcp" to-port="22" to-addr='"172.16.0.2"

# firewall-cmd --reload

# firewall-cmd --list-all

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp4s0f0
  sources: 
  services: ssh dhcpv6-client
  ports: 8139/tcp
  protocols: 
  masquerade: yes
  forward-ports: port=222:proto=tcp:toport=22:toaddr=172.16.0.2
  source-ports: 
  icmp-blocks: 
  rich rules: 
     rule family="ipv4" destination address="0.0.0.0/0" forward-port port="222" protocol="tcp" to-port="22" to-addr="172.16.0.2" 

# firewall-cmd --direct --get-all-rules

ipv4 filter INPUT 0 -d 0.0.0.0/0 -j ACCEPT
ipv4 filter OUTPUT 0 -d 0.0.0.0/0 -j ACCEPT
ipv4 filter FORWARD 0 -d 0.0.0.0/0 -j ACCEPT
ipv4 nat PREROUTING 0 -d 0.0.0.0/0 -p tcp --dport 222 -j DNAT --to-destination 172.16.0.2:22

Now, when trying to connect to the guest – from my laptop, via host port 222 – the ssh connection is refused:

ssh -l stack my-host -p 222
ssh: connect to host my-host port 222: Connection refused

Any idea what am I missing ?

Best Answer

You could try firewall-cmd --add-forward-port:

firewall-cmd --permanent --add-forward-port=port=222:proto=tcp:toaddr=172.x.x.x:toport=22
firewall-cmd --reload

General syntax (https://firewalld.org/documentation/man-pages/firewall-cmd.html):

firewall-cmd [--permanent] [--zone=zone] \
 --add-forward-port=port=portid[-portid]:proto=protocol[:toport=portid[-portid]][:toaddr=address[/mask]] \
 [--timeout=timeval]

EDIT: this quick howto might be all you need, but as comments point out, this doesn't fix the OP's problem. Check Eduardo's comment for a possible solution if you're using libvirt.