はじめに
以前の記事で書いたlibvirtのフックを利用して、NATでポートフォワードする方法を示す。 古いブログなどではiptables-save/iptables-restoreを利用してポートフォワードの設定を永続化している例もあるが、フックを使う方式が正しいと思われる。
サンプルのポートフォワードのフック
まずは、libvirtのドキュメントにサンプル掲載されている、ポートフォワードのフックを示す。 サンプルなので仕方がないが、このままだとポートフォワードの設定を変更するたびにスクリプトの変更が必要になる。
#!/bin/bash # IMPORTANT: Change the "VM NAME" string to match your actual VM Name. # In order to create rules to other VMs, just duplicate the below block and configure # it accordingly. if [ "${1}" = "VM NAME" ]; then # Update the following variables to fit your setup GUEST_IP= GUEST_PORT= HOST_PORT= if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then /sbin/iptables -D FORWARD -o virbr0 -d $GUEST_IP -j ACCEPT /sbin/iptables -t nat -D PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT fi if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then /sbin/iptables -I FORWARD -o virbr0 -d $GUEST_IP -j ACCEPT /sbin/iptables -t nat -I PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT fi fi
Pythonでリライトしたポートフォワードのフック
前出のサンプルの欠点を埋めるために、ポートフォワードの設定部分を以下のようなYAMLで外だしするようにした。
"web-server": - bridge: virbr0 vm_ip: 192.168.8.4 forwardings: - host_port: 10022 vm_port: 22 - host_port: 10080 vm_port: 80 "other-vm": - bridge: virbr0 vm_ip: 192.168.8.8 forwardings: - host_port: 20022 vm_port: 22
さすがにbashでYAMLはキビシイので、サンプルスクリプトをPythonでリライトした。
以下のスクリプトを/etc/libvirt/hooks/qemu
という名前で作成し、実行権を与えておく。
そして、このスクリプトと同じディレクトリに、上記のようなYAMLをport-forwarding.yml
というファイル名で配置しておくと、これを読み込んで適切にiptablesを実行する。
#!/usr/bin/python3 import os import os.path import sys import subprocess import yaml def run_command(command): print(command, file=sys.stderr) subprocess.run(command) def entries(entries): for entry in entries: yield (entry, entry["bridge"], entry["vm_ip"]) def forwardings(entry): vm_ip = entry["vm_ip"] for forwarding in entry["forwardings"]: yield (str(forwarding["host_port"]), vm_ip + ":" + str(forwarding["vm_port"])) def on_libvirt_hook(config, vm_name, operation, sub_operation, extra_argument): if vm_name in config: for entry, bridge, vm_ip in entries(config[vm_name]): if operation in ["stopped", "reconnect"]: run_command(["/sbin/iptables", "-D", "FORWARD" ,"-o", bridge, "-d", vm_ip, "-j", "ACCEPT"]) for host_port, vm_ip_and_port in forwardings(entry): run_command(["/sbin/iptables", "-t", "nat", "-D", "PREROUTING", "-p", "tcp", "--dport", host_port, "-j", "DNAT", "--to", vm_ip_and_port]) if operation in ["start", "reconnect"]: run_command(["/sbin/iptables", "-I", "FORWARD" ,"-o", bridge, "-d", vm_ip, "-j", "ACCEPT"]) for host_port, vm_ip_and_port in forwardings(entry): run_command(["/sbin/iptables", "-t", "nat", "-I", "PREROUTING", "-p", "tcp", "--dport", host_port, "-j", "DNAT", "--to", vm_ip_and_port]) if __name__ == '__main__': config_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "port-forwarding.yml") with open(config_file) as f: config = yaml.load(f) on_libvirt_hook(config, sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
動作確認
スクリプト/etc/libvert/hooks/qemu
をコマンドラインから直接実行して、iptablesの変化を見てみる。
まずは、スクリプト実行前のiptablesの状態を以下に示す。
$ sudo 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 192.168.8.0/24 ctstate RELATED,ESTABLISHED ACCEPT all -- 192.168.8.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 ACCEPT udp -- anywhere anywhere udp dpt:bootpc $ sudo iptables -L -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination RETURN all -- 192.168.8.0/24 base-address.mcast.net/24 RETURN all -- 192.168.8.0/24 255.255.255.255 MASQUERADE tcp -- 192.168.8.0/24 !192.168.8.0/24 masq ports: 1024-65535 MASQUERADE udp -- 192.168.8.0/24 !192.168.8.0/24 masq ports: 1024-65535 MASQUERADE all -- 192.168.8.0/24 !192.168.8.0/24
そして、以下のようにweb-serverというVMを起動した想定で、スクリプトを実行する。
$ sudo /etc/libvirt/hooks/qemu web-server start - - ['/sbin/iptables', '-I', 'FORWARD', '-o', 'virbr0', '-d', '192.168.8.4', '-j', 'ACCEPT'] ['/sbin/iptables', '-t', 'nat', '-I', 'PREROUTING', '-p', 'tcp', '--dport', '10022', '-j', 'DNAT', '--to', '192.168.8.4:22'] ['/sbin/iptables', '-t', 'nat', '-I', 'PREROUTING', '-p', 'tcp', '--dport', '10080', '-j', 'DNAT', '--to', '192.168.8.4:80']
スクリプト実行後のiptablesの状態は以下のようになり、適切にエントリが追加されていることが分かる。
$ sudo 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 192.168.8.4 ←●追加された ACCEPT all -- anywhere 192.168.8.0/24 ctstate RELATED,ESTABLISHED ACCEPT all -- 192.168.8.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 ACCEPT udp -- anywhere anywhere udp dpt:bootpc $ sudo iptables -L -t nat Chain PREROUTING (policy ACCEPT) target prot opt source destination DNAT tcp -- anywhere anywhere tcp dpt:amanda to:192.168.8.4:80 ←●追加された DNAT tcp -- anywhere anywhere tcp dpt:10022 to:192.168.8.4:22 ←●追加された Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination RETURN all -- 192.168.8.0/24 base-address.mcast.net/24 RETURN all -- 192.168.8.0/24 255.255.255.255 MASQUERADE tcp -- 192.168.8.0/24 !192.168.8.0/24 masq ports: 1024-65535 MASQUERADE udp -- 192.168.8.0/24 !192.168.8.0/24 masq ports: 1024-65535 MASQUERADE all -- 192.168.8.0/24 !192.168.8.0/24
参考
- Networking - Forwarding Incoming Connections