EC2 仅允许 VPN 的规则:每个关联的安全组都必须一致
安全组只会放行,而每个关联的安全组都会加入自己的权限。先确认实例真正看到的来源,再加一条狭窄的 VPN 规则。
你为 VPN 客户端地址池添加了一条入站规则,没有删除任何规则,服务却依然可以从公网访问。并没有哪里坏掉。安全组没有覆盖、没有优先级,也没有一条最终的拒绝能让那条狭窄规则赢过它。实例上关联的每个安全组都会把自己的放行项汇入同一个合并集合,而你忘掉的那条宽泛规则仍然在这个集合里。要让实例只接受 VPN,意味着审计全部安全组,而不是在其中某一个上再加一条听起来更合理的规则。
写规则之前先把服务写清楚
先确定精确的目标地址、协议和端口,以及服务真正监听的那块网络接口。有多个接口、或同一端口上跑着多个服务的实例,需要按接口分别审计,因为关联的安全组可能并不相同。然后再确定 该接口对 VPN 用户实际收到的源地址,而不是客户端在自己连接窗口里显示的那个地址。
一旦涉及地址转换和路由,这两个地址分道扬镳的频率比人们预想的要高。
三种来源模型,三条不同的规则
在私有路由访问下,实例收到的是客户端地址池或企业地址段,具体取决于路径如何被路由和转换。当通过企业出口访问公网端点时,实例看到的反而是企业 NAT 地址,而且只有该目标确实走了 VPN 路径时才成立,因为分离隧道可能让它留在家庭宽带上。在使用托管的 Client VPN 端点时,适用文档所述的关联与目标网络模型,客户端地址来自配置的客户端 CIDR。
行不通的做法是把 VPN 网关当作安全组的来源。组引用匹配的是 VPC 内或对等网络中的弹性网络接口,而转发数据包的网关并不属于其中。
读取所有关联安全组的并集
打开实例,找到它的网络接口,列出关联到该接口的每一个安全组。对每一个安全组,检查所有覆盖你所用协议和端口的规则,包括宽端口范围和两个地址族。来自 0.0.0.0/0 的 IPv4 规则并不能免除你检查 ::/0,而放行全部流量的规则会彻底掩盖端口这个问题。只有在完成这份清单之后,你才能说该服务的实际权限是什么。
AWS 的规则参考文档把安全组描述为只允许,并指出所有关联安全组的规则会被聚合,而这正是这次故障背后的全部机制。
网络 ACL 是另一套无状态策略,带有有序的允许和拒绝条目,它在子网层面评估,而不是在实例层面。
不要用拒绝之墙来回答
为了模仿你习惯的防火墙而在整个子网加一条 NACL 拒绝,是错误的工具,而且有风险。NACL 条目有序且无状态,所以放在既有允许之上的一条拒绝会悄无声息地破坏无关流量,包括你本想保留的那些会话的回程路径。要在拥有该权限的那个安全组里修正宽泛的放行,然后把 ACL 放到一边,除非它确实是设计的一部分。
从实例上移除公网地址是更强的保证,而它同时是一项架构变更,会带来运维和应用层面的后果,而不是允许列表上的一个勾选框。
用新会话测试,并保留一条进入通道
在撤销任何现有放行之前,先确认一条已批准的独立管理通道,因为你即将删除的那条规则,可能正是会话恢复所依赖的那一条。然后用一条全新的被允许连接从 VPN 侧测试,再用一条全新的被拒绝连接从已获授权的非 VPN 测试路径测试。现有已被追踪的会话会以某种方式在规则变更中存活,这使旧的开着的 shell 无法作为验收证据。
最后,保留应用自身的授权。一次转换后的企业出口可能代表数百人,而网络层的准入从未识别出某个具体用户或其设备。