KVM forward ports to guests VM with UFW on Linux

last updated in Categories , , , ,

My Debian/Ubuntu/CentOS Linux server using KVM as a hypervisor. I would like to forward ports to guests VM with UFW iptables. How do I forward ports on a Linux server running libvirt/KVM to specified ports on VM’s, when using NAT? How do I configure KVM/libvirt forward ports to guests with iptables?

This page shows how to forward ports to guests VM in libvirt/KVM running on CentOS 7 or Debian 9 or Ubuntu Linux LTS server using ufw. For example, the host has a public IP of 202.54.1.4 . You want to forward all ports to 192.168.122.253. Say if you are using NAT-ed networking. You want to allow external access to services offered by your VMs. This page shows the process of NAT port forwarding to VMs using ufw running on Linux.

KVM forward ports to guests VM with UFW on Linux

Our sample setup is as follows:
Sample setup KVM libvirt Forward Ports to guests with UFW Iptables
Where,

  • My server runs CentOS 7 latest
  • KVM installed on CentOS 7 server
  • The server has a total 4 VMs with private IP address
  • CentOS7 server has total 5 public IP address
  • Your task is to forward all ports traffic coming to 202.54.1.4 to 192.168.122.253 CentOS7 VM1.
  • Next forward ssh traffic coming to 202.54.1.5 VM2 at 192.168.122.125.

Find out information about your KVM network

Type the following command:
# virsh net-list
# virsh net-info default
# virsh net-dumpxml default

virsh get info about kvm NAT networking

Understanding KVM networking and default iptables/ufw rules

From the above commands, it is clear that I have a virtual network (virbr0) set in nat mode. In nat mode, all VMs can make outgoing connections. Each guest VM in default network can talk to each other too. The host os can talk to all guest VMs also. However, all connections comming from the Internet or other hosts on LAN blocked. The default rule is set as follows by KVM/libvirt:
iptables -A FORWARD -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
You need to update FORWARD as follows to accept new connection for each VM in nat mode:
iptables -A FORWARD -s 192.168.2.0/24 -d 192.168.122.0/24 -o virbr0 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

Step 1 – Configure kvm firewall hook

The default KVM NAT config provides a rule, but it omits the NEW state, which is essential for accepting new incoming connections. To solve this problem create a hook as follows:
# cd /etc/libvirt/hooks/
Create a file named qemu using a text editor such as vim command or nano command
# vi qemu
Append code as follows:

#!/bin/bash
# Hook to insert NEW rule to allow connection for VMs
# 192.168.122.0/24 is NATed subnet
# virbr0 is networking interface for VM and host
# -----------------------------------------------------------------
# Written by Vivek Gite under GPL v3.x {https://www.cyberciti.biz}
# -----------------------------------------------------------------
# get count
v=$(/sbin/iptables -L FORWARD -n -v | /usr/bin/grep 192.168.122.0/24 | /usr/bin/wc -l)
# avoid duplicate as this hook get called for each VM
[ $v -le 2 ] && /sbin/iptables -I FORWARD 1 -o virbr0 -m state -s 192.168.2.0/24 -d 192.168.122.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT

Save and close the file in VIM/vi text editor. Set permission using the chmod command:
# chmod +xv /etc/libvirt/hooks/qemu

Step 2 – Configuring the UFW (Iptables firewall) to port forward

Edit the /etc/ufw/before.rules files using a text editor:
Next forward ssh traffic coming to 202.54.1.5 VM2 at 192.168.122.125.
# vim /etc/ufw/before.rules
Add at the top of the file:

# KVM/libvirt Forward Ports to guests with Iptables (UFW) #
*nat
:PREROUTING ACCEPT [0:0]
-A PREROUTING -d 202.54.1.4 -p tcp --dport 1:65535 -j DNAT --to-destination 192.168.122.253:1-65535 -m comment --comment "VM1/CentOS 7 ALL ports forwarding"
-A PREROUTING -d 202.54.1.5 -p tcp --dport 22 -j DNAT --to-destination 192.168.122.125:22 -m comment --comment "VM2/OpenBSD SSH port forwarding"
-A PREROUTING -d 202.54.1.6 -p tcp --dport 443 -j DNAT --to-destination 192.168.122.231:443 -m comment --comment "VM3/FreeBSD 443 port forwarding"
-A PREROUTING -d 202.54.1.7 -p tcp --dport 80 -j DNAT --to-destination 192.168.122.229:80 -m comment --comment "VM4/CentOS 80 port forwarding"
COMMIT

Save and close the file in a vim/vi text editor. You can reload rules with the following commands or simple reboot the Linux server:
# bash /etc/libvirt/hooks/qemu
# ufw reload

OR
# reboot

Step 3 – Verify that forwarding ports to guests in libvirt/KVM working

You can list iptables rules using the following syntax. First make sure NEW rules at the top of the FORWARD chain:
# iptables -L FORWARD -nv --line-number

KVM forward ports to guests VM with UFW on Linux iptables rules
Click to enlarge image

Next make sure connections from outside forwarded to each VM using DNAT set in /etc/ufw/before.rules file. You can list nat/DNAT prerouting rule using the iptables command:
# iptables -t nat -L PREROUTING -n -v --line-number
# iptables -t nat -L -n -v

centos kvm redirect traffic iptables
Click to enlarge image

Please note that the MASQUERADE rule is automatically added by the KVM/libvirt. Try ping command or curl command or ssh command to access VMs from outside:
$ ping 202.54.1.4
$ curl -I 202.54.1.4
# login to VM2/openbsd
$ ssh vivek@202.54.1.5

Linux forwarding ports to guests in libvirt / KVM iptables rules

If you are not using UFW, here is a dump of actual iptables rules:
# iptables-save -t nat
Sample outputs:

# Generated by iptables-save v1.4.21 on Tue Jul 31 03:14:18 2018
*nat
:PREROUTING ACCEPT [72:5501]
:INPUT ACCEPT [42:3301]
:OUTPUT ACCEPT [32:4304]
:POSTROUTING ACCEPT [32:4304]
-A PREROUTING -d 202.54.1.4/32 -p tcp -m tcp --dport 1:65535 -m comment --comment "VM1/CentOS 7 ALL ports forwarding" -j DNAT --to-destination 192.168.122.253:1-65535
-A PREROUTING -d 202.54.1.5/32 -p tcp -m tcp --dport 22 -m comment --comment "VM2/OpenBSD SSH port forwarding" -j DNAT --to-destination 192.168.122.125:22
-A PREROUTING -d 202.54.1.6/32 -p tcp -m tcp --dport 443 -m comment --comment "VM3/FreeBSD 443 port forwarding" -j DNAT --to-destination 192.168.122.231:443
-A PREROUTING -d 202.54.1.7/32 -p tcp -m tcp --dport 80 -m comment --comment "VM4/CentOS 80 port forwarding" -j DNAT --to-destination 192.168.122.229:80
-A POSTROUTING -s 192.168.122.0/24 -d 224.0.0.0/24 -j RETURN
-A POSTROUTING -s 192.168.122.0/24 -d 255.255.255.255/32 -j RETURN
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE
COMMIT

And FORWARD rules from kvm hook shell script
# iptables-save -t filter | grep FORWARD
Sample outputs:

:FORWARD DROP [0:0]
-A FORWARD -s 192.168.2.0/24 -d 192.168.122.0/24 -o virbr0 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT
-A FORWARD -i virbr0 -o virbr0 -j ACCEPT
-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable

Conclusion

You just learned how to use KVM to forward ports to guests VM with UFW on Linux. If you are a new to UFW, see this page on how to setup UFW on Ubuntu and setup UFW on a CentOS 7 server here.

Posted by: Vivek Gite

The author is the creator of nixCraft and a seasoned sysadmin, DevOps engineer, and a trainer for the Linux operating system/Unix shell scripting. Get the latest tutorials on SysAdmin, Linux/Unix and open source topics via RSS/XML feed or weekly email newsletter.

Notable Replies

  1. yes, I would update page soon with newer hook info. Thanks!

Continue the discussion www.nixcraft.com

1 more reply

Participants