前言:之前连载过 4.4.2 使用网桥模式(bridge networking mode) 一文,其实使用NAT的情况也蛮多的,不需要建物理的bridge配置会更加方便,所以现在把KVM NAT也连载出来。(两个脚本qemu-ifup-NAT和qemu-ifdown-NAT,请到github下载:https://github.com/smilejay/kvm-book/tree/master/scripts)
NAT(Network Addresss Translation ,网络地址转换),属于广域网接入技术的一种,它将内网地址转化为外网的合法IP地址,它被广泛应用于各种类型的Internet接入方式和各种类型的网络之中。NAT将来自内网IP数据包的包头中的源IP地址转换为一个外网的IP地址。众所周知,IPv4的地址资源已几近枯竭,而NAT使内网的多个主机可以共用一个IP地址接入网络,这样有助于节约IP地址资源,这也是NAT最主要的作用。另外,通过NAT访问外部网络的内部主机,其内部IP对外是不可见的,这就隐藏了NAT内部网络拓扑结构和IP信息,也就能够避免内部主机受到外部网络的攻击。客观事物总是有正反两面性的,没有任何技术是十全十美的。NAT技术隐藏了内部主机细节从而提高了安全性,但是如果NAT内的主机作为Web或数据库服务器需接受来自外部网络的主动连接,这时NAT就表现出了局限性,不过,可以在拥有外网IP的主机上使用iptables等工具实现端口映射,从而让外网对这个外网IP的一个端口的访问被重新映射到NAT内网的某个主机的相应端口上去。
在QEMU/KVM中,默认使用IP伪装的方式去实现NAT,而不是使用SNAT(Source-NAT)或DNAT(Destination-NAT)的方式。图4-6展示了KVM中的NAT模式网络的结构图,宿主机在外网的IP是10.10.10.190,其上运行的各个客户机的IP属于内网的网络段192.168.122.0/24。
图4-6 KVM中的NAT模式网络
在KVM中配置客户机的NAT网络方式,需要在宿主机中运行一个DHCP服务器给宿主机分配NAT内网的IP地址,可以使用dnsmasq工具来实现。在KVM中,DHCP服务器为客户机提供服务的基本架构如图4-7所示。
图4-7 宿主机中的dnsmasq为客户机提供DHCP服务
通过下面几步可以使客户机启动并以NAT方式配置好它的网络。
1) 检查配置宿主机内核编译的配置,将网络配置选项中与NAT相关的选项配置好,否则在启动客户机使用NAT网络配置时可能会遇到如下错误提示,因为无法按需加载“iptable_nat”和“nf_nat”等模块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
iptables v1.4.7: can't initialize iptables table `nat': Table does not exist (do you need to insmod?) 遇到这样的情况,只能重新配置和编译内核了。下面截取的一小段内核配置,是一般情况下NAT的部分相关配置。 # # IP: Netfilter Configuration # CONFIG_NF_DEFRAG_IPV4=m CONFIG_NF_CONNTRACK_IPV4=m CONFIG_NF_CONNTRACK_PROC_COMPAT=y CONFIG_IP_NF_QUEUE=m CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_RPFILTER=m CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_TARGET_ULOG=m CONFIG_NF_NAT=m CONFIG_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=m CONFIG_IP_NF_TARGET_NETMAP=m CONFIG_IP_NF_TARGET_REDIRECT=m |
2) 安装必要的软件包:bridge-utils、iptables和dnsmasq等。其中bridge-utils包含管理bridge的工具brctl(在4.5.2节中已使用过),iptables是对内核网络协议栈中IPv4包的过滤工具和NAT管理工具,dnsmasq是一个轻量级的DHCP和DNS服务器软件。当然,如有其他满足类似功能的软件包,也可以选用。在宿主机中,查看所需软件包情况,如下:
1 2 3 4 5 6 7 |
[root@jay-linux kvm_demo]# rpm -qa | grep bridge bridge-utils-1.2-9.el6.x86_64 [root@jay-linux kvm_demo]# rpm -qa | grep iptables iptables-ipv6-1.4.7-4.el6.x86_64 iptables-1.4.7-4.el6.x86_64 [root@jay-linux kvm_demo]# rpm -qa | grep dnsmasq dnsmasq-2.48-5.el6.x86_64 |
3) 准备一个为客户机建立NAT用的qemu-ifup脚本及关闭网络用的qemu-ifdown脚本。这两个脚本中的$1(传递给它们的第一个参数)就是在客户机中使用的网络接口在宿主机中的虚拟网络名称(如tap0、tap1等)。
其中,在启动客户机时建立网络的脚本示例(/etc/qemu-ifup-NAT)如下,主要功能是:建立bridge,设置bridge的内网IP(此处为192.168.122.1),并且将客户机的网络接口与其绑定,然后打开系统中网络IP包转发的功能,设置iptables的NAT规则,最后启动dnsmasq作为一个简单的DHCP服务器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
#!/bin/bash # qemu-ifup script for QEMU/KVM with NAT netowrk mode # set your bridge name BRIDGE=virbr0 # Network information NETWORK=192.168.122.0 NETMASK=255.255.255.0 # GATEWAY for internal guests is the bridge in host GATEWAY=192.168.122.1 DHCPRANGE=192.168.122.2,192.168.122.254 # Optionally parameters to enable PXE support TFTPROOT= BOOTP= function check_bridge() { if brctl show | grep "^$BRIDGE" &> /dev/null; then return 1 else return 0 fi } function create_bridge() { brctl addbr "$BRIDGE" brctl stp "$BRIDGE" on brctl setfd "$BRIDGE" 0 ifconfig "$BRIDGE" "$GATEWAY" netmask "$NETMASK" up } function enable_ip_forward() { echo 1 > /proc/sys/net/ipv4/ip_forward } function add_filter_rules() { iptables -t nat -A POSTROUTING -s "$NETWORK"/"$NETMASK" \ ! -d "$NETWORK"/"$NETMASK" -j MASQUERADE } function start_dnsmasq() { # don't run dnsmasq repeatedly ps -ef | grep "dnsmasq" | grep -v "grep" &> /dev/null if [ $? -eq 0 ]; then echo "Warning:dnsmasq is already running." return 1 fi dnsmasq \ --strict-order \ --except-interface=lo \ --interface=$BRIDGE \ --listen-address=$GATEWAY \ --bind-interfaces \ --dhcp-range=$DHCPRANGE \ --conf-file="" \ --pid-file=/var/run/qemu-dhcp-$BRIDGE.pid \ --dhcp-leasefile=/var/run/qemu-dhcp-$BRIDGE.leases \ --dhcp-no-override \ ${TFTPROOT:+"--enable-tftp"} \ ${TFTPROOT:+"--tftp-root=$TFTPROOT"} \ ${BOOTP:+"--dhcp-boot=$BOOTP"} } function setup_bridge_nat() { check_bridge "$BRIDGE" if [ $? -eq 0 ]; then create_bridge fi enable_ip_forward add_filter_rules "$BRIDGE" start_dnsmasq "$BRIDGE" } # need to check $1 arg before setup if [ -n "$1" ]; then setup_bridge_nat ifconfig "$1" 0.0.0.0 up brctl addif "$BRIDGE" "$1" exit 0 else echo "Error: no interface specified." exit 1 fi 关闭客户机时调用的网络脚本示例(/etc/qemu-ifdown-NAT)如下,它主要完成解除bridge绑定、删除bridge和清空iptalbes的NAT规则。 #!/bin/bash # qemu-ifdown script for QEMU/KVM with NAT network mode # set your bridge name BRIDGE="virbr0" if [ -n "$1" ]; then echo "Tearing down network bridge for $1" ip link set $1 down brctl delif "$BRIDGE" $1 ip link set "$BRIDGE" down brctl delbr "$BRIDGE" iptables -t nat -F exit 0 else echo "Error: no interface specified" exit 1 fi |
当然,对于这两个脚本中实现的功能,可以根据实际情况进行修改,另外,手动来完成这样的功能而不依赖于这两个脚本一样是可行的。
4) 当启动客户机时,使用上面提到的启动脚本。创建客户机的qemu-kvm命令行如下:
qemu-system-x86_64 rhel6u3.img -m 1024 -smp 2 -net nic -net \ tap,script=/etc/qemu-ifup-NAT,downscript=/etc/qemu-ifdown-NAT
在启动客户机后,检查脚本中描述的宿主机中的各种配置生效的情况,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
[root@jay-linux kvm_demo]# brctl show bridge name bridge id STP enabled interfaces br0 8000.60eb692129b7 yes eth0 virbr0 8000.4a826f3a76d5 yes tap0 #注意区别这两个bridge的不同:br0是前面提到的网桥模式使用的,它与一个物理上的网络接口eth0绑定,而virbr0是这里介绍的NAT方式的bridge,它没有绑定任何物理网络接口,只是绑定了tap0这个客户机使用的虚拟网络接口 [root@jay-linux kvm_demo]# iptables -t nat -L 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 MASQUERADE all -- 192.168.122.0/24 !192.168.122.0/24 [root@jay-linux ~]# ifconfig virbr0 virbr0 Link encap:Ethernet HWaddr 4A:82:6F:3A:76:D5 inet addr:192.168.122.1 Bcast:192.168.122.255 Mask:255.255.255.0 inet6 addr: fe80::4882:6fff:fe3a:76d5/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:59 errors:0 dropped:0 overruns:0 frame:0 TX packets:49 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:7537 (7.3 KiB) TX bytes:6471 (6.3 KiB) [root@jay-linux ~]# ps -ef | grep dnsmasq | grep -v grep nobody 22507 1 0 Jul27 ? 00:00:08 dnsmasq --strict-order --except-interface=lo --interface=virbr0 --listen-address=192.168.122.1 --bind-interfaces --dhcp-range=192.168.122.2,192.168.122.254 --conf-file= --pid-file=/var/run/qemu-dhcp-virbr0.pid --dhcp-leasefile=/var/run/qemu-dhcp-virbr0.leases --dhcp-no-override |
5) 在客户机中,通过DHCP动态获得IP,并且检查网络是否畅通,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
[root@kvm-guest ~]# dhclient eth0 eth0: link up, 100Mbps, full-duplex, lpa 0x05E1 [root@kvm-guest ~]# ifconfig eth0 eth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56 inet addr:192.168.122.140 Bcast:192.168.122.255 Mask:255.255.255.0 inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:112 errors:0 dropped:0 overruns:0 frame:0 TX packets:108 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:12073 (11.7 KiB) TX bytes:18654 (18.2 KiB) Interrupt:11 Base address:0xa000 [root@kvm-guest ~]# route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.122.0 * 255.255.255.0 U 0 0 0 eth0 default 192.168.122.1 0.0.0.0 UG 0 0 0 eth0 [root@kvm-guest ~]# ping 192.168.122.1 -c 1 PING 192.168.122.1 (192.168.122.1) 56(84) bytes of data. 64 bytes from 192.168.122.1: icmp_seq=1 ttl=64 time=0.373 ms --- 192.168.122.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.373/0.373/0.373/0.000 ms [root@kvm-guest ~]# ping 192.168.199.103 -c 1 PING 192.168.199.103 (192.168.199.103) 56(84) bytes of data. 64 bytes from 192.168.199.103: icmp_seq=1 ttl=63 time=0.947 ms --- 192.168.199.103 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 1ms rtt min/avg/max/mdev = 0.947/0.947/0.947/0.000 ms |
从上面的命令行输出可知,客户机可以通过DHCP获得网络IP(192.168.122.0/24子网中),其默认网关是宿主机的bridge的IP(192.168.122.1),并且可以ping通网关(192.168.122.1)和子网外的另外一个主机(192.168.199.103),说明其与外部网络的连接正常。
另外,客户机中的DNS服务器默认配置为宿主机(192.168.122.1),如果宿主机没有启动DNS服务,则可能导致在客户机中无法解析域名。这时需要将客户机中/etc/resolv.conf修改为与宿主机中一致可用的DNS配置,然后就可以正常解析外部的域名(主机名)了,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[root@kvm-guest ~]# vi /etc/resolv.conf [root@kvm-guest ~]# cat /etc/resolv.conf ; generated by /sbin/dhclient-script search tsp.org nameserver 192.168.199.3 [root@kvm-guest ~]# nslookup vt-snb9 Server: 192.168.199.3 Address: 192.168.199.3#53 Name: vt-snb9.tsp.org Address: 192.168.199.99 [root@kvm-guest ~]# ping vt-snb9 -c 1 PING vt-snb9.tsp.org (192.168.199.99) 56(84) bytes of data. 64 bytes from 192.168.199.99: icmp_seq=1 ttl=63 time=0.741 ms --- vt-snb9.tsp.org ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 2ms rtt min/avg/max/mdev = 0.741/0.741/0.741/0.000 ms |
6) 添加iptables规则进行端口映射,让外网主机也能访问客户机。
到步骤5)为止,客户机已可以正常连通外部网络,但是外部网络(除宿主机外)无法直接连接到客户机。其中一个解决方案是,在宿主机中设置iptables的规则进行端口映射,使外部主机对宿主机IP的一个端口的请求转发到客户机中的某一个端口。
在宿主机中,查看网络配置情况,然后iptables设置端口映射将如下,将宿主机的80端口(常用于HTTP服务)映射到客户机的80端口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[root@jay-linux kvm_demo]# route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default sqa-gate.tsp.or 0.0.0.0 UG 0 0 0 br0 192.168.0.0 * 255.255.0.0 U 0 0 0 br0 192.168.122.0 * 255.255.255.0 U 0 0 0 virbr0 [root@jay-linux kvm_demo]# ifconfig br0 br0 Link encap:Ethernet HWaddr 60:EB:69:21:29:B7 inet addr:192.168.82.0 Bcast:192.168.255.255 Mask:255.255.0.0 inet6 addr: fe80::ccc7:84ff:fe41:280c/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:608315 errors:0 dropped:0 overruns:0 frame:0 TX packets:49539 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:72662298 (69.2 MiB) TX bytes:79644268 (75.9 MiB) [root@jay-linux kvm_demo]# iptables -t nat -A PREROUTING -p tcp –d \ 192.168.82.0 --dport 80 -j DNAT --to 192.168.122.140:80 [root@jay-linux kvm_demo]# iptables -t nat -L Chain PREROUTING (policy ACCEPT) target prot opt source destination DNAT tcp -- anywhere 192.168.82.0 tcp dpt:http to:192.168.122.140:80 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 MASQUERADE all -- 192.168.122.0/24 !192.168.122.0/24 |
在客户机中,编辑一个在HTTP服务中被访问的示例文件(/var/www/html/index.html,Apache默认根目录为/var/www/html),然后启动Apache服务。
[root@kvm-guest ~]# cat /var/www/html/index.html
This an index page demo running on apache in kvm-guest.
[root@kvm-guest ~]# service httpd start
Starting httpd: httpd: apr_sockaddr_info_get() failed for kvm-guest
httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
[ OK ]
在外部网络某主机上测试连接宿主机(192.168.82.0)的80端口,就会被映射到客户机(192.168.122.140)中的80端口,如图4-8所示,外部网络已经可以正常访问在NAT内网中的那台客户机80端口上的HTTP服务了。
图4-8 外部网络主机访问宿主机而被映射到客户机中相应的端口
在上面的示例中,NAT的配置涉及的一些iptables配置规则仅用于实验演示,在实际生产环境中需要根据实际情况进行更细粒度的配置,如果将访问规则和数据包转发规则设置得过于宽松可能会带来网络安全方面的隐患。