何十もの記事、Blob、チュートリアルを読んで、stackoverflowに関する質問に答えた後でも、ホストからゲストVMにポート転送を設定する方法という問題にはまだ答えがありません。
まず、英語が苦手で申し訳ありません。はっきりと言うように努力します。
第二:私はネットワーキングの完全初心者です。ただし、同僚のためにこのサーバーを設定する必要があります。
私たちは、Webソフトウェアに複数のテスト環境を提供するために3つのKVM仮想マシンを設定したいパブリックホスティングCentos 7サーバーを持っています。私の考えは、各仮想マシンに転送するポート範囲を割り当てることです。たとえば、ポート10001:19999はVM 1の1:9999に転送され、ポート20001:29999はVM 2の1:9999に転送される式です。 。
私は多くの解決策を試しましたが、そのうち何も動作しませんでした。これは私の現在の設定です。
#> ifconfig
eno2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet xx.xxx.xx.xxx netmask 255.255.255.0 broadcast xx.xxx.xx.255
ether aa:aa:aa:aa:aa:aa txqueuelen 1000 (Ethernet)
RX packets 10190055 bytes 644136763 (614.2 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 338010 bytes 27222247 (25.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device memory 0x92b00000-92bfffff
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 2283 bytes 4633913 (4.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2283 bytes 4633913 (4.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
virbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255
ether bb:bb:bb:bb:bb:bb txqueuelen 1000 (Ethernet)
RX packets 4448 bytes 566487 (553.2 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3374 bytes 1243921 (1.1 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
vnet0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether cc:cc:cc:cc:cc:cc txqueuelen 1000 (Ethernet)
RX packets 268 bytes 23314 (22.7 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2071 bytes 114034 (111.3 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
#> cat /etc/sysctl.conf
# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv4.ip_forward = 1
#> cat /etc/libvirt/hooks/qemu
#!/bin/bash
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 -lt 1 ] && /sbin/iptables -I FORWARD 1 -o virbr0 -m state -s xx.xxx.xx.xxx/32 -d 192.168.122.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT
update(){
if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
/sbin/iptables -t nat -D PREROUTING 1 -d $GUEST_IP -p tcp --dport $HOST_PORT -j DNAT --to-destination $GUEST_IP:$GUEST_PORT -m comment --comment "$1 VM port forwarding"
fi
if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
/sbin/iptables -t nat -I PREROUTING 1 -d $GUEST_IP -p tcp --dport $HOST_PORT -j DNAT --to-destination $GUEST_IP:$GUEST_PORT -m comment --comment "$1 VM port forwarding"
fi
}
GUEST_PORT=1-9999
if [ "${1}" = "VM1" ]; then
GUEST_IP=192.168.122.101
HOST_PORT=10001:19999
elif [ "${1}" = "VM2" ]; then
GUEST_IP=192.168.122.102
HOST_PORT=20001:29999
fi
update $1 $2
#>virsh net-edit default
<network>
<name>default</name>
<uuid>0db10b13-21c6-45c3-a891-ec46509b2121</uuid>
<forward mode='nat'/>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='aa:aa:aa:aa:aa:aa'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac='bb:cc:dd:ee:ff:01' name='VM1' ip='192.168.122.101'/>
<host mac='bb:cc:dd:ee:ff:02' name='VM2' ip='192.168.122.102'/>
</dhcp>
</ip>
</network>
qemuフックはうまく機能しているようで、iptablesのルールは私が望んでいた通りです。
#> iptables -L FORWARD -nv --line-number
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT all -- * virbr0 xx.xxx.xx.xxx 192.168.122.0/24 state NEW,RELATED,ESTABLISHED
2 185 13612 ACCEPT all -- virbr0 * 192.168.122.0/24 0.0.0.0/0
3 0 0 ACCEPT all -- virbr0 virbr0 0.0.0.0/0 0.0.0.0/0
4 186 13704 REJECT all -- * virbr0 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
5 0 0 REJECT all -- virbr0 * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
6 0 0 ACCEPT all -- * virbr0 xx.xxx.xx.xxx 192.168.122.0/24 state NEW,RELATED,ESTABLISHED
7 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
#> iptables -t nat -L -n -v
Chain PREROUTING (policy ACCEPT 25580 packets, 2244K bytes)
pkts bytes target prot opt in out source destination
0 0 DNAT tcp -- * * 0.0.0.0/0 192.168.122.101 tcp dpts:10001:19999 /* VM1 port forwarding */ to:192.168.122.101:1-9999
Chain INPUT (policy ACCEPT 774 packets, 46800 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 578 packets, 44429 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 578 packets, 44429 bytes)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- * * 192.168.122.0/24 224.0.0.0/24
0 0 RETURN all -- * * 192.168.122.0/24 255.255.255.255
4 240 MASQUERADE tcp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
159 12084 MASQUERADE udp -- * * 192.168.122.0/24 !192.168.122.0/24 masq ports: 1024-65535
0 0 MASQUERADE all -- * * 192.168.122.0/24 !192.168.122.0/24
しかし、私のコンピュータからVM1にアクセスすると仮定し、SSHを介して自分の仮想マシンにアクセスしようとすると
ssh [email protected]:10022
動作しません。
私は何を見逃していますか?
答え1
さまざまな質問
この回路図を読むと、パケットで発生する作業の順序を理解し、次のガイドラインを理解するのに役立ちます。
filter/FORWARD
:DNATパケットを正しく許可します。
現在、この規則は次のとおりです。
... /sbin/iptables -I FORWARD 1 -o virbr0 -m state -s xx.xxx.xx.xxx/32 -d 192.168.122.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT
一致しません:転送(ルーティング)時に、パケットはホストアドレスを持つソースを持たないか、ホストによって放出されますが、PREROUTINGフックを通過しません(ただしOUTPUT)。nat/PREROUTING
ソースではなくターゲットを変更してください。
リモートアクセスを許可するには、許可されている各リモートソースのルールを指定するか(ホストのxx.xxx.xx.xxx/32を許可されたリモートクライアントyy.yyy.yy.yyyのアドレスに置き換えます)、指定するか、すべてのソースを指定しないでください。リモートクライアントを許可するには、次の手順を実行します。
/sbin/iptables -I FORWARD 1 -o virbr0 -d 192.168.122.0/24 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
具体的に一致させたい場合、パケットは最初に宛先xx.xxx.xx.xxx/32(たとえば、複数のパブリックアドレスを持ち、この役割専用アドレスを持つホストに便利です)に到着し、次に192.168.122.0/に一致するものに変換されます。 。 24はまだ可能ですconntrack
(OPのstate
一致の置き換え):
/sbin/iptables -I FORWARD 1 -o virbr0 -d 192.168.122.0/24 -m conntrack --ctstate NEW,RELATED,ESTABLISHED --ctorigdst xx.xxx.xx.xxx/32 -j ACCEPT
他の可能性もあります。最も簡単な方法は、以前のルールですでに実行されているDNAT変換を経たフローのパケット部分を簡単に受け入れることです。これらのDNATは、流れが許容される場合にのみ有用であるからである。
/sbin/iptables -I FORWARD 1 -m conntrack --ctstate DNAT -j ACCEPT
nat/PREROUTING
:元のターゲットは仮想マシンのアドレスではありません。
仮想マシンに直接アクセスできないため、クライアントは192.168.122.101:10022に接続しようとしません。 192.168.122.101に直接接続できる場合は、192.168.122.101:22に接続してこの質問をする必要はありません。
クライアントはホストの単一のパブリックIP(または代替パブリックIP)に接続します。スポンサーのiptablesその後、ルールはポートをIPとポートに変換します。したがって、ホストにxx.xxx.xx.xxxアドレスがある場合、ルールはnat/PREROUTING
VMのIPターゲットと一致しないでください。代わりに、ホストのIPターゲットと一致しようとする必要があります。これが発生すると、filter/FORWARD
最終目的地が表示されます(前のポイントで述べたように)。
最後に、以下を使用しないでください。
/sbin/iptables -t nat -I PREROUTING 1 -d $GUEST_IP -p tcp --dport $HOST_PORT -j DNAT --to-destination $GUEST_IP:$GUEST_PORT -m comment --comment "$1 VM port forwarding"
ただし、たとえば次のようになります(複雑さを避けるために受信インターフェイスも指定)。
/sbin/iptables -t nat -I PREROUTING 1 -i eno2 -d xx.xxx.xx.xxx/32 -p tcp --dport $HOST_PORT -j DNAT --to-destination $GUEST_IP:$GUEST_PORT -m comment --comment "$1 VM port forwarding"
nat/OUTPUT および nat/PREROUTING
ホストでローカルに発生するパケットは、転送(ルーティング)されたパケットとは異なる方法で処理されます。
OPの追加規則はにありますnat/PREROUTING
。PREROUTING
パケットが受信されると(そしてルーティング決定が行われる前に)発生します。具体的には、nat/PREROUTING
以下でのみ発生します。最初接続ストリームパケット(例:すべてナットフック)最初のパケットが送信されず、受信された場合にのみ可能です。
から取得しようとすると離れてシステム(いいえライブラリ仮想マシンホスト)OPのルールを正しく実行する必要があります。テストは常に最終ユースケースのように実行する必要があります。リモートアクセス用のものであれば、テストはリモートで行わなければなりません(そうであるかもしれませんが、OPでは指定されていません)。
ホストでテストすると、これは異なります。最初パケットは受信したパケットではなく、送信されたパケットです。通過しませんPREROUTING
。その後、仮想マシンの応答はもはやストリームの最初のパケットではありません。つながるstatus NEW
)上記のようにNATはNetfilterによって完全に処理されるため、これらすべてをスキップすると結果は次のようになります。つながる入り口。前の図は以下を示しています。
「新しい」接続についてのみ「NAT」表を参照してください。
したがって、ホストの場合、nat/PREROUTING
前のポイントと比較して変更された点は次のとおりです。
/sbin/iptables -t nat -I PREROUTING 1 -i eno2 -d xx.xxx.xx.xxx/32 -p tcp --dport $HOST_PORT -j DNAT --to-destination $GUEST_IP:$GUEST_PORT -m comment --comment "$1 VM port forwarding"
トリガーされていません:変更効果はまだ利用できないため、ターゲットはまだxx.xxx.xx.xxxです。それを変えるのがまさにこのルールだ。
で行われた同様の設定は、nat/PREROUTING
以下でも実行する必要がありますnat/OUTPUT
。
iptables -t nat -I OUTPUT 1 -d xx.xxx.xx.xxx/32 -p tcp --dport $HOST_PORT -m comment --comment "VM1 port forwarding" -j DNAT --to-destination $GUEST_IP:$GUEST_PORT -m comment --comment "$1 VM port forwarding test from host"
今正しく一致します。
一方、127.0.0.1の代わりにxx.xxx.xx.xxxを使用すると、ポート10001-19999のホストTCPサービスに独自にアクセスできなくなります。ホストが直接接続されているため、-d xx.xxx.xx.xxx/32
OP の元のルールを置き換えるルールは引き続き使用できますが、$GUEST_IP
ルールセットをテストする以外にはあまり役に立ちません。
しかし、とにかく、すべてのケースで...
iptables静的ポート範囲マッピングを実行できません。
...これで、ほとんどすべての試みが失敗する可能性が高くConnection refused
、そうでない場合、仮想マシンで予想されるサービスが達成されない可能性が高くなります。
... -p tcp --dport 10001:19999 -j DNAT --to 192.168.122.101:1-9999
結果は、魔法のようにポート値から10000を減算しません。使用可能な任意のポートを選択します(たとえば、以前のポートと一致しません)。つながる各ユニークストリームの1〜9999の範囲のエントリ)。したがって、ポート10022はポート22に変換されず、6456などの範囲内の任意の値に変換され、連続して新しい接続を試行するたびに異なるポート値になります。
ベースのソリューションはありません。iptablesこれを行うには、9999のルール(各ポートごとに1つ)を追加する必要があることを除いて。特にポート22の場合、次のように動作します。
... -p tcp -m tcp --dport 10022 -j DNAT --to 192.168.122.101:22
したがって、実際には小さなポートセットを選択し、一度に1つのルールで個々のポートマッピングを実行する必要があります。 VM1 が HTTP 1.x サーバーの場合、ポート 22、ポート 80、ポート 443 の 3 つのルールを使用できます (ただし、ホストの HTTP リバース プロキシがより良いソリューションである可能性があります)。
... -p tcp -m tcp --dport 10022 -j DNAT --to 192.168.122.101:22
... -p tcp -m tcp --dport 10080 -j DNAT --to 192.168.122.101:80
... -p tcp -m tcp --dport 10443 -j DNAT --to 192.168.122.101:443
ボーナス:静的ポート範囲マッピングのためのより簡単な方法
ボーナスで静的ポート変換を実行する方法について説明します。CentOS 7では動作しません必要な機能が不足しているからです。しかし近いうちに(2024-06-30)CentOS 7を交換する必要があります...
IPセット
次の2つの理由で使用できません。
いいえIPセットタイプには2つのポートがあり、アドレスに加えて1つのポートから別のポートにマッピングできます。
さらに重要なのは、iptables'
DNAT
ターゲットには、このサブシステムを使用するための規制はありません。
nftables
試験用nftables1.0.2とカーネル5.16.x。
これには最新バージョンが必要です。nftablesそしてカーネル。 CentOS 7は適していません。nftablesカーネル3.13から利用可能です。 CentOS 7はカーネル3.10を使用します。nftablesすでにRed Hatカーネル機能がバックポートされています。より新しいものnftablesツールとカーネルに機能がありません。
特に古いカーネルではnftablesそしてiptables(従来の)は特にNATフックと衝突するため、NATを実行するために一緒に使用することはできませんが(登録に失敗するか自動的に無視されます)、最新のカーネルでは一緒に機能します。
このfilter/FORWARD
セクションと一般MASQUERADE
規則もそのまま残すことができます。iptables、仮想マシンへのポート変換を処理するNATのみを使用してください。nftables。
ビット単位の演算
おそらく最も難しいオプションです。nftablesそしてカーネルバージョン(ただし、CentOS 7ではカーネル3.10では不十分です)。
nftablesより多くの機能がありますが、まだ減算できないため、10000からtcpポートを減算することはできません。ただし、ビット単位の操作は非常にうまく機能します。したがって、10000 個のポート範囲を割り当てるのではなく、範囲が 2 の倍数でソートされると、この静的ポートマッピングを実現できます。 10000の範囲に近い場合は、8192(65536/8192-1 =合計7台の仮想マシン)または16384(合計3台の仮想マシン)を使用できます。
16384を使用します。最初の使用可能範囲は16384-32767(0x4000-0x7fff)で、ネットマスクは16383(0x3fff)です。ポート変換はターゲットポートの最初の範囲(0〜16383)にマッピングされるため、
|
ビットAND(下)に加えてビットOR()を適用する必要はありません。&
優先順位-110は、次の優先順位を持つために使用されます。iptables'同様のフックの場合、優先順位は-100です。 NATの場合nftablesNATフックの不一致、iptablesいつものように、NATに対応するフックは後で一致する機会があります。
nft add table rangenat nft add chain rangenat prerouting '{ type nat hook prerouting priority -110; }' nft add chain rangenat output '{ type nat hook output priority -110; }'
最初の仮想マシンの場合:
nft add rule rangenat prerouting 'ip daddr xx.xxx.xx.xxx/32 tcp dport 16384-32767 dnat to 192.168.122.101:tcp dport & 0x3fff' nft add rule rangenat output 'ip daddr xx.xxx.xx.xxx/32 tcp dport 16384-32767 dnat to 192.168.122.101:tcp dport & 0x3fff'
このコマンドと同様に、ポート16384 + 22 = 16406からアドレス192.0.2.2のホストに接続されているアドレス203.0.113.11のクライアントを使用してモデルテストを実行します(OPの構文が正しくないため、ポートをパラメータとして指定する必要があります
-p
)。ssh -p 16406 [email protected]
結果はこれらにあります。つながる
conntrack -E
TCP 3方向ハンドシェイク中にホストに表示される項目:[NEW] tcp 6 120 SYN_SENT src=203.0.113.11 dst=192.0.2.2 sport=42458 dport=16406 [UNREPLIED] src=192.168.122.101 dst=203.0.113.11 sport=22 dport=42458 [UPDATE] tcp 6 60 SYN_RECV src=203.0.113.11 dst=192.0.2.2 sport=42458 dport=16406 src=192.168.122.101 dst=203.0.113.11 sport=22 dport=42458 [UPDATE] tcp 6 432000 ESTABLISHED src=203.0.113.11 dst=192.0.2.2 sport=42458 dport=16406 src=192.168.122.101 dst=203.0.113.11 sport=22 dport=42458 [ASSURED]
応答ポートsrc(2番目の部分)が期待
sport=22
どおりに表示されます。-
必要nftables 0.9.4接続によるNATマッピング(およびタイプ構文)とカーネル5.6。
nftables'
dnat
ステートメントを使用できます。地図変化を助けます(その間iptables'DNAT
ターゲットは利用できませんIPセット)。以前のスケルトンを使用すると、次のようになります。
nft add table rangenat nft add chain rangenat prerouting '{ type nat hook prerouting priority -110; }' nft add chain rangenat output '{ type nat hook output priority -110; }'
地図を追加:
nft add map rangenat port2ipport '{ typeof tcp dport : ip daddr . tcp dport; }'
これらの一般的な規則は次のとおりです。
nft add rule rangenat prerouting 'ip daddr xx.xxx.xx.xxx/32 dnat ip to tcp dport map @port2ipport nft add rule rangenat output 'ip daddr xx.xxx.xx.xxx/32 dnat ip to tcp dport map @port2ipport
次に、ループを使用するたびに次のように入力します。
nft add element rangenat port2ipport '{ 10001: 192.168.122.101 . 1 }' ... nft add element rangenat port2ipport '{ 10022: 192.168.122.101 . 22 }' ... nft add element rangenat port2ipport '{ 19999: 192.168.122.101 . 9999 }' nft add element rangenat port2ipport '{ 20001: 192.168.122.102 . 1 }' ...
または、次のように最小限の必須部分を組み合わせるだけです。
nft add element rangenat port2ipport '{ 10022: 192.168.122.101 . 22, 10080: 192.168.122.101 . 80, 10443: 192.168.122.101 . 443, 20022: 192.168.122.102 . 22, 30022: 192.168.122.103 . 22 }'
ハッシュ値で一般的な検索時間は地図これはO(1)であり、これはビット操作ベースの以前の方法と似ています。ポートごとに1つのルールを使用する最も簡単な方法iptablesまたはnftablesO(n)ルックアップ時間が発生し、これは何千ものルールのパフォーマンスに影響を与え始めることがあります。