在 libvirt/KVM 中将端口转发给客户机

在 libvirt/KVM 中将端口转发给客户机

使用 NAT 时,如何将运行 libvirt/KVM 的服务器上的端口转发到 VM 上的指定端口?

例如主机有公网IP 1.2.3.4,我想将80端口转发到10.0.0.1,将22端口转发到10.0.0.2。

我认为我需要添加 iptables 规则,但我不确定哪里合适以及应该具体指定什么。

iptables -L 的输出

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     udp  --  anywhere             anywhere            udp dpt:domain 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:domain 
ACCEPT     udp  --  anywhere             anywhere            udp dpt:bootps 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:bootps 

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             10.0.0.0/24         state RELATED,ESTABLISHED 
ACCEPT     all  --  10.0.0.0/24          anywhere            
ACCEPT     all  --  anywhere             anywhere            
REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 
REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

ifconfig 的输出

eth0      Link encap:Ethernet  HWaddr 00:1b:fc:46:73:b9  
          inet addr:192.168.1.14  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::21b:fcff:fe46:73b9/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:201 errors:0 dropped:0 overruns:0 frame:0
          TX packets:85 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:31161 (31.1 KB)  TX bytes:12090 (12.0 KB)
          Interrupt:17 

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

virbr1    Link encap:Ethernet  HWaddr ca:70:d1:77:b2:48  
          inet addr:10.0.0.1  Bcast:10.0.0.255  Mask:255.255.255.0
          inet6 addr: fe80::c870:d1ff:fe77:b248/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:468 (468.0 B)

我正在使用 Ubuntu 10.04。

答案1

Ubuntu 上 libvirt 的最新稳定版本是 0.7.5 版,它没有一些新功能(例如脚本挂钩和网络过滤器),这些功能使自动网络配置更加容易。话虽如此,以下是如何在 Ubuntu 10.04 Lucid Lynx 上为 libvirt 0.7.5 启用端口转发。

这些 iptables 规则应该可以解决问题:

iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT

默认的 KVM NAT 配置提供了与我上面给出的第三条规则类似的规则,但它省略了 NEW 状态,这对于接受传入连接至关重要。

如果您编写启动脚本来添加这些规则,而您又不小心,libvirt 0.7.5 会通过插入自己的规则来覆盖它们。因此,为了确保这些规则在启动时正确应用,您需要确保 libvirt 已初始化你插入你的规则。

在 /etc/rc.local 中的以下行之前添加以下行exit 0

(
# Make sure the libvirt has started and has initialized its network.
while [ `ps -e | grep -c libvirtd` -lt 1 ]; do
        sleep 1
done
sleep 10
# Set up custom iptables rules.
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -I PREROUTING -p tcp -d 1.2.3.4 --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT
) &

以上sleep 10是确保 libvirt 守护进程有机会在我们添加自己的 iptables 规则之前初始化其 iptables 规则的技巧。我迫不及待地想看到他们为 Ubuntu 发布 libvirt 版本 0.8.3。

答案2

有一种方法可以动态设置端口重定向当客户机使用用户模式网络时,我在这里写了一篇博客:

http://blog.adamspiers.org/2012/01/23/port-redirection-from-kvm-host-to-guest/

您可以在那里看到详细信息,但为了方便起见,这里是我找到的解决方案:

virsh qemu-monitor-command --hmp sles11 'hostfwd_add ::2222-:22'

这个单行答案比其他答案容易得多,但仅在某些情况下有效(用户模式网络堆栈)。

答案3

一个更“官方”[1] 的方法是创建一个钩子脚本,如 libvirt 网站上所述:

http://wiki.libvirt.org/page/Networking#Forwarding_Incoming_Connections

... 基本上,当 KVM 客户机启动时,将调用此脚本。脚本本身将添加适当的 iptable 规则(类似于上面 Isaac Sutherland 的回答),并正确添加“NEW”连接状态。请注意,您必须使用适合您的主机和端口的正确值修改脚本。

[1] 尽管 libvirt 文档本身说这是一种 hack,但想想看

答案4

在 Ubuntu 20.04 上我编写了以下脚本,保存为/etc/libvirt/hooks/allow-portfw(chmod +x):

#!/bin/bash

CHAIN=LIBVIRT_FWI
IPTCMD="iptables -L $CHAIN -vn"
FILTERCMD="grep -v -e Chain -e pkts -e reject-with -e DNAT"

while $IPTCMD | grep ESTABLISHED | grep -v DNAT >/dev/null
do
    IF=$($IPTCMD | $FILTERCMD | awk '{print $7}' | head -n1)
    NET=$($IPTCMD | $FILTERCMD | awk '{print $9}' | head -n1)
    iptables -D $CHAIN -o $IF -d $NET -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    iptables -I $CHAIN 1 -o $IF -d $NET -m conntrack --ctstate DNAT,RELATED,ESTABLISHED -j ACCEPT
done

进入的情况是,我们缺少新的。FORWARDING 链的 DNAT 标志,它是自动创建的。

sudo iptables -L LIBVIRT_FWI -vn
Chain LIBVIRT_FWI (1 references)
 pkts bytes target     prot opt in     out     source               destination         
1741K 2661M ACCEPT     all  --  *      virbr0  0.0.0.0/0            192.168.122.0/24     ctstate RELATED,ESTABLISHED
   38  1972 REJECT     all  --  *      virbr0  0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable

我的脚本将 DNAT 标志添加到所有定义中。

这样,我只需在 iptables 中添加端口转发即可

iptables -A PREROUTING -i mylanIF -p tcp --dport 3389 -j DNAT --to-destination 192.168.122.110:3389

您可以使用软件包iptables-persistent来保存此状态(但一定要从转储中删除 libvirt 链)

或者您可以使用ufw任何其他防火墙脚本。

我的脚本基于此地址的发现https://www.cyberciti.biz/faq/kvm-forward-ports-to-guests-vm-with-ufw-on-linux/

相关内容