概览
基于我之前的文章中的相关内容,本文通过使用OVN访问控制列表(ACL)来实现基本的网络安全功能。
OVN访问控制列表和地址集介绍
OVN中的ACL规则存储于北向数据库的ACL表中,并且可以使用ovn-nbctl的acl命令进行配置。目前,ACL只能应用于逻辑交换机,但是未来将支持应用到逻辑路由器。
在出站和入站方向都支持使用ACL:
-
入站:从工作负载(from-lport)进入逻辑端口
-
出站:从逻辑端口出去到工作负载(to-lport)。
此外,为每个ACL分配一个优先级,以确定它们的匹配顺序,最高优先级首先被匹配。另外ACL可以被赋予相同的优先级。 然而,在两个具有相同优先级并且都匹配同一个分组的ACL的情况下,将仅匹配一个ACL。确切地说,哪一个ACL最终将被匹配是不确定的,你不能真正地确定哪个规则将应用于给定的情况。所以我建议:在大多数情况下尽量使用不同的优先级。
ACL中的匹配规则基于OVS中的流语法,对于具有编程背景的人都会觉得很熟悉。该语法在ovn-sb手册页的“Logical_Flow表”部分中进行了说明。它值得一读,特别是介绍“!=”匹配规则的那部分内容。
另外值得强调的一点是,您不能在具有type = router的端口上创建ACL匹配规则。为了减少ACL表中的条目数量,可以使用定义相同类型地址组的地址集。例如,一组IPv4地址/网络,一组MAC地址或一组IPv6地址可以放置在一个可命名的地址集内。为了减少ACL表中的条目数,可以使用定义相同类型地址组的地址集。 例如,一组IPv4地址/网络,一组MAC地址或一组IPv6地址可以放置在命名地址集内。 然后地址集可以被ACL的match子句内的“name”引用(以$ name的形式)。
下面举个例子:
| #允许来自交换机“ls1”上端口“ls1-vm1”的所有ip流量,同时允许相关回包通过 ovn-nbctl acl-add ls1 from-lport 1000 "inport == \"ls1-vm1\" && ip" allow-related
# 允许 ssh 到 ls1-vm1 ovn-nbctl acl-add ls1 to-lport 999 "outport == \"ls1-vm1\" && tcp.dst == 22" allow-related
# 阻止所有到ls1-vm1的IPv4/IPv6流量 ovn-nbctl acl-add ls1 to-lport 998 "outport == \"ls1-vm1\" && ip" drop |
注意“allow-related”的使用。 它的作用是这样:它允许反向相关的流量(例如,响应,fragements等)通过。 在第二条规则中,为了允许从服务器回来的SSH响应流量通过,我就使用了 allow-related 。
我们再来看看地址集。 如前文所述,地址集是相同类型的地址组。 使用ovn-nbctl北向数据库命令创建地址集,然后在ACL中调用地址集。 这里有几个创建地址集的例子:
| ovn-nbctl create Address_Set name=wwwServers addresses=172.16.1.2,172.16.1.3 ovn-nbctl create Address_Set name=www6Servers addresses=\"fd00::1\",\"fd00::2\" ovn-nbctl create Address_Set name=macs addresses=\"02:00:00:00:00:01\",\"02:00:00:00:00:02\" |
注意使用带有包含‘:’字符的地址集的双引号。 如果你在命令中不使用双引号,将会报错。
实验环境
我们在实验室环境中使用ACL进行实验。详细的环境介绍和搭建,请参考我之前的文章。
物理网络环境:
OVN逻辑网络环境:
实验一
第一步,我们将通过为每个服务器创建一个静态NAT规则来允许从外部访问DMZ中的服务器。
在ubuntu1上执行:
| #为vm1创建snat-dnat规则并应用于edge1 ovn-nbctl -- --id=@nat create nat type="dnat_and_snat" logical_ip=172.16.255.130 \ external_ip=10.127.0.250 -- add logical_router edge1 nat @nat
#为vm2创建snat-dnat规则并应用于edge1 ovn-nbctl -- --id=@nat create nat type="dnat_and_snat" logical_ip=172.16.255.131 \ external_ip=10.127.0.251 -- add logical_router edge1 nat @nat |
从 ubuntu1 ping snat-dnat 的外部IP:
| root@ubuntu1:~# ping 10.127.0.250 PING 10.127.0.250 (10.127.0.250) 56(84) bytes of data. 64 bytes from 10.127.0.250: icmp_seq=1 ttl=62 time=2.57 ms 64 bytes from 10.127.0.250: icmp_seq=2 ttl=62 time=1.23 ms 64 bytes from 10.127.0.250: icmp_seq=3 ttl=62 time=0.388 ms
root@ubuntu1:~# ping 10.127.0.251 PING 10.127.0.251 (10.127.0.251) 56(84) bytes of data. 64 bytes from 10.127.0.251: icmp_seq=1 ttl=62 time=3.15 ms 64 bytes from 10.127.0.251: icmp_seq=2 ttl=62 time=1.52 ms 64 bytes from 10.127.0.251: icmp_seq=3 ttl=62 time=0.475 ms |
第二步:检查虚拟机可以使用正确的IP连接到外部世界
| root@ubuntu2:~# ip netns exec vm1 ping 10.127.0.130 PING 10.127.0.130 (10.127.0.130) 56(84) bytes of data. 64 bytes from 10.127.0.130: icmp_seq=1 ttl=62 time=3.05 ms
root@ubuntu3:~# ip netns exec vm2 ping 10.127.0.130 PING 10.127.0.130 (10.127.0.130) 56(84) bytes of data. 64 bytes from 10.127.0.130: icmp_seq=1 ttl=62 time=1.87 ms
root@ubuntu1:~# tcpdump -i br-eth1 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on br-eth1, link-type EN10MB (Ethernet), capture size 262144bytes 17:51:01.055258 IP 10.127.0.250 > 10.127.0.130: ICMP echo request, id4565, seq 12, length 64 17:51:01.055320 IP 10.127.0.130 > 10.127.0.250: ICMP echo reply, id 4565, seq 12, length 64
17:51:56.378089 IP 10.127.0.251 > 10.127.0.130: ICMP echo request, id4301, seq 6, length 64 17:51:56.378160 IP 10.127.0.130 > 10.127.0.251: ICMP echo reply, id 4301, seq 6, length 64 |
现在,我们可以从ubuntu1上的tcpdump的输出结果看到虚拟机正在使用正确的NAT地址。
第三步:应用一些安全策略。 首先,我们将完全锁定DMZ。
| #添加默认ACL策略(对不匹配任何转发规则的流量进行丢弃) ovn-nbctl acl-add dmz to-lport 900 "outport == \"dmz-vm1\" && ip" drop ovn-nbctl acl-add dmz to-lport 900 "outport == \"dmz-vm2\" && ip" drop |
在ubuntu1上验证网络已经不通:
| root@ubuntu1:~# ping 10.127.0.250 PING 10.127.0.250 (10.127.0.250) 56(84) bytes of data. ^C --- 10.127.0.250 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1007ms
root@ubuntu1:~# ping 10.127.0.251 PING 10.127.0.251 (10.127.0.251) 56(84) bytes of data. ^C --- 10.127.0.251 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1007ms |
DMZ服务器现在无法从外部访问,但这时候从DMZ内部ping 外界网络也不通了:
| root@ubuntu2:~# ip netns exec vm1 ping 10.127.0.130 PING 10.127.0.130 (10.127.0.130) 56(84) bytes of data. ^C --- 10.127.0.130 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1008ms
root@ubuntu3:~# ip netns exec vm2 ping 10.127.0.130 PING 10.127.0.130 (10.127.0.130) 56(84) bytes of data. ^C --- 10.127.0.130 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 1008ms |
我们来解决这个问题:
| #允许所有ip流量和相关连接回包通过 ovn-nbctl acl-add dmz from-lport 1000 "inport == \"dmz-vm1\" && ip" allow-related ovn-nbctl acl-add dmz from-lport 1000 "inport == \"dmz-vm2\" && ip" allow-related |
验证:
| root@ubuntu2:~# ip netns exec vm1 ping 10.127.0.130 PING 10.127.0.130 (10.127.0.130) 56(84) bytes of data. 64 bytes from 10.127.0.130: icmp_seq=1 ttl=62 time=4.16 ms 64 bytes from 10.127.0.130: icmp_seq=2 ttl=62 time=3.07 ms
root@ubuntu3:~# ip netns exec vm2 ping 10.127.0.130 PING 10.127.0.130 (10.127.0.130) 56(84) bytes of data. 64 bytes from 10.127.0.130: icmp_seq=1 ttl=62 time=3.59 ms 64 bytes from 10.127.0.130: icmp_seq=2 ttl=62 time=2.30 ms |
允许到DMZ服务器的入站HTTPS (tcp 443端口)流量:
| #允许到DMZ服务器的入站HTTPS (tcp 443端口)和回包流量: ovn-nbctl acl-add dmz to-lport 1000 "outport == \"dmz-vm1\" && tcp.dst == 443" allow-related ovn-nbctl acl-add dmz to-lport 1000 "outport == \"dmz-vm2\" && tcp.dst == 443" allow-related |
验证: 首先需要监听tcp 443 端口。我喜欢使用ncat。 它实际上是nmap软件包的一部分。所以第一步是在所有3个Ubuntu主机上安装它:
| apt-get -y install nmap |
现在我们可以在443端口运行一个监听进程。进程将在连接结束时终止,但是你可以使用-k参数来持续运行它。
在ubuntu2:
| ip netns exec vm1 ncat -l -p 443 |
在 ubuntu3:
| ip netns exec vm2 ncat -l -p 443 |
并在ubuntu1的检查连通性。 如果连接成功,它将保持打开状态,直到您终止它。 如果没有,那么它应该在1秒后超时
| root@ubuntu1:~# ncat -w 1 10.127.0.250 443 ^C
root@ubuntu1:~# ncat -w 1 10.127.0.251 443 ^C |
策略生效了! 我们加固了“inside”服务器的安全性。
实验二
接下来的操作会使它更加安全:阻止所有出站访问,并且只允许从dmz访问tcp 3306。 我们将配置一个地址集来表示DMZ内的一些地址。 注意在“acl-add”命令中使用了单引号。 我们实际上是通过地址集名称来引用它,前缀是一个'$'字符。 因为我们不希望bash将其解释为一个变量,所以我们使用了单引号。
| # 为dmz服务器创建一个地址集。 使用/31掩码 ovn-nbctl create Address_Set name=dmz addresses=\"172.16.255.130/31\"
# 允许源IP为dmz地址集内的IP地址,且目标端口为3306 ovn-nbctl acl-add inside to-lport 1000 'outport == "inside-vm3" && ip4.src == $dmz && tcp.dst == 3306' allow-related ovn-nbctl acl-add inside to-lport 1000 'outport == "inside-vm4" && ip4.src == $dmz && tcp.dst == 3306' allow-related
# 添加默认ACL策略(对不匹配任何转发规则的流量进行丢弃) ovn-nbctl acl-add inside to-lport 900 "outport == \"inside-vm3\" && ip" drop ovn-nbctl acl-add inside to-lport 900 "outport == \"inside-vm4\" && ip" drop |
我们将再次使用ncat监听我们的VM,但这次是在vm3 /vm4上进行
在 ubuntu2:
| ip netns exec vm3 ncat -l -p 3306 |
在 ubuntu3:
| ip netns exec vm4 ncat -l -p 3306 |
验证 dmz到 inside的网络连通性:
| root@ubuntu2:~# ip netns exec vm1 ncat -w 1 172.16.255.195 3306 ^C
root@ubuntu3:~# ip netns exec vm2 ncat -w 1 172.16.255.194 3306 ^C |
貌似生效了。 最后再来确认vm3 /vm4彼此隔离的:
| root@ubuntu2:~# ip netns exec vm3 ncat -w 1 172.16.255.195 3306 Ncat: Connection timed out.
root@ubuntu3:~# ip netns exec vm4 ncat -w 1 172.16.255.194 3306 Ncat: Connection timed out. |
vm3 /vm4确认是彼此隔离的,上面的ACL策略的确生效了。实验成功!
清理环境
删除ACL和地址集。
在 ubuntu1:
| ovn-nbctl acl-del dmz ovn-nbctl acl-del inside ovn-nbctl destroy Address_Set dmz |
结语
传统防火墙是在路径中的第三层设备上实现的,例如路由器或专用防火墙设备。而在OVN在逻辑交换机上应用策略可能看起来就很奇怪了。然而这种方法的优点在于,它通过在逻辑端口级别实施安全性,创建了一种以东西(east-west )方式使工作负载更安全的简单方法。这种安全方法已经被称为“微分段”(又译作“微分割”),因为它允许管理员以非常精细的方式应用安全策略。传统网络设计(认为web-tier / db-tier)中存在的大多数层级是由于网络安全性以前只能在位于层之间的某个中央设备上完成。微分段方法的本质是:允许将网络设计扁平化到靠一个逻辑交换机来实现一切:安全策略和网络布局。
在下一篇文章中,我将介绍用于容器网络的OVN模型。
文章翻译自:http://blog.spinhirne.com/2016/10/ovn-and-acls.html