
使用 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/