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 (, behind host NAT).

Using iptables on Host - it will work:

# iptables -I OUTPUT  -d -j ACCEPT
# iptables -I FORWARD  -d -j ACCEPT
# iptables -I INPUT  -d -j ACCEPT
# iptables -t nat -I PREROUTING -d -p tcp --dport 222 -j DNAT --to-destination

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='
# firewall-cmd --permanent --zone=public --add-masquerade
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -d -j ACCEPT
# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -d -j ACCEPT
# firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -d -j ACCEPT

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

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

# firewall-cmd --reload

# firewall-cmd --list-all

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

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

ipv4 filter INPUT 0 -d -j ACCEPT
ipv4 filter OUTPUT 0 -d -j ACCEPT
ipv4 filter FORWARD 0 -d -j ACCEPT
ipv4 nat PREROUTING 0 -d -p tcp --dport 222 -j DNAT --to-destination

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 ?

  • Your first three rules effectively disable all firewalling. Why have you put these rules in place? Sep 7, 2018 at 12:05
  • @MichaelHampton - I tried everything that could make this ssh tunnel work via firewalld, and as you see, even those first three rules did not open the tunnel.
    – Noam Manos
    Sep 20, 2018 at 8:59
  • 1
    I've open bugzilla 1590924 for this issue.
    – Noam Manos
    Sep 20, 2018 at 9:17
  • Did you ever come to a resolution? I see the bug doesn't have one noted either.
    – flickerfly
    Jul 3, 2019 at 21:13
  • 1
    @flickerfly - not yet, I'm still using iptables directly, and try to avoid reboots (that requires reset of iptables)
    – Noam Manos
    Jul 18, 2019 at 22:36

4 Answers 4


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]] \

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.

  • 3
    I did try that, it's already included in my steps above - It doesn't work (Connection refused when trying to ssh to port 222).
    – Noam Manos
    Sep 20, 2018 at 9:13
  • This approach does not work! Here a complete thread about this problem: superuser.com/q/1474254/195840 . Aug 22, 2019 at 20:48

Have faced a similar issue personally and this solution has come to my rescue. Please give it a try and let me know if it helped.

There are few ways to access a server behind NAT: Port forwarding, you can configure the router/firewall to forward the incoming traffic to an internal server. Usually, you need to specify the Protocol (UDP/TCP), External Service Port, and Internal Service Port.

For the ssh port forwarding with firewall-cmd, please try this command:

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 222 -j DNAT \--to 172.x.x.x.:22 

From the given code, I kind of understand that this is what you are trying to achieve. So, please make sure to check the port. In my case, I have tried the same with port 80.

This rule specifies the NAT table to use the built-in PREROUTING chain to forward incoming HTTP requests exclusively to the listed destination IP address

  • 2
    Thanks @james, that's an "iptables" command, not "firewall-cmd". I can achieve ssh tunnel with iptables, but not with firewalld.
    – Noam Manos
    Jun 14, 2018 at 19:11

It sounds like your problems aren't a problem with your firewall-cmd commands, but something to do with OpenStack?

For reference, I'll post the final response (by James Slagle, presumably this one) on the Bugzilla report you made - just in case it helps someone:

As suggested by Eric, this issue is related to OSP deployment, thus changing bug owners to TripleO.

Note that it is known to effect OSP 14 and bellow.

No, this is not correct based on how I interpret the bug. You're trying to add firewall rules on the host (titan08) that is hosting a vm called undercloud-0.

undercloud-0 is where OpenStack is installed. But there are no OpenStack rpm's or configuration done on the physical host itself (titan08).

In that case, it seems that this is entirely about the configuration of the firewall of the physical host and not the OpenStack vm.

As Eric pointed out, the rules you are trying to add with firewalld are not getting saved on the physical host (titan08).

Is my understanding correct?

When you install OpenStack on a VM, it makes no changes to the physical host itself.


The "symptoms" and the data you present are very similar to what we experienced using KVM (libvirt) with the "default network". For this reason we find the answer below very pertinent...

The "only" way we can make a port forward using KVM (libvirt) with the "default network" (virbr0) is using the hack/workaround informed by @Antony Nguyen . Or more simply you can use libvirt-hook-qemu.

This thread has a complete explanation of how to solve this problem for CentOS 7 (and certainly for other distros) using libvirt-hook-qemu: https://superuser.com/a/1475915/195840 .

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.