將連接埠轉送到 libvirt / KVM 中的 guest 虛擬機

將連接埠轉送到 libvirt / KVM 中的 guest 虛擬機

使用 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 規則。我等不及他們為 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 文件本身表示這是一種駭客行為,但請弄清楚

答案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/

相關內容