3. 安全组与网络ACL实战

在 AWS 的 VPC 中,安全组(Security Group,SG)和网络 ACL(Network ACL,NACL)是两道作用位置、工作方式完全不同的过滤层。很多生产事故并非因为“没有配规则”,而是因为把两者的语义搞混:以为 NACL 也像 SG 一样“出去放行、回来自动放行”,结果只开了入站 443,返回流量被无状态丢弃,表现为“TCP 三次握手成功但应用超时”。理解这两层,是设计可预测网络边界的前提。

一、作用位置与状态语义

安全组附着在弹性网卡(ENI)上,作用于实例级别,是有状态的:只要入站规则允许某个连接建立,该连接的返回流量会被自动放行,无需为出站方向再写一条对应规则。网络 ACL 附着在子网边界上,作用于子网级别,是无状态的:入站和出站方向各自独立评估,返回流量必须由对方向的规则显式允许。

两者都只支持“允许”规则,没有显式拒绝(NACL 的默认规则除外),因此判断逻辑是“命中任意允许规则即通过,否则丢弃”。安全组默认拒绝所有入站、允许所有出站;NACL 默认规则是编号 100 允许全部、编号 * 拒绝全部,自定义规则按编号从小到大依次匹配,命中即停止。

安全组(有状态)与网络ACL(无状态)过滤路径对比 Internet 客户端 203.0.113.9 网络ACL(子网边界) 无状态 · 双向独立 入站 100 允许 443 出站 100 允许 1024-65535 * 拒绝全部 返回流量需显式放行 安全组(ENI) 有状态 · 返回自动放行 入站 443 来自 0.0.0.0/0 出站 默认全部允许 返回流量自动放行 仅允许规则,无显式拒绝 EC2 实例 Nginx :443 排错观察点 VPC Flow Logs ACCEPT / REJECT SG 规则命中计数 NACL 规则编号顺序 tcpdump / ss -tn Reachability Analyzer SYN SYN SYN SYN-ACK SYN-ACK SYN-ACK 关键差异 · 安全组:连接建立后返回流量自动放行,规则可引用其他安全组 ID,支持动态成员。 · 网络ACL:入站与出站分别评估,返回流量必须由对方向规则显式允许,规则按编号顺序匹配。 · 典型误配:NACL 只放行入站 443,未放行出站临时端口,导致握手成功但应用超时。

二、规则设计:从“能通”到“最小权限”

安全组规则设计应遵循“引用优先、网段其次、0.0.0.0/0 最后”的原则。生产环境常见的三层结构是:面向公网的 ALB 安全组只允许 443 来自 0.0.0.0/0;应用层安全组只允许来自 ALB 安全组 ID 的流量;数据库安全组只允许来自应用层安全组 ID 的 3306。这样即使应用层实例被替换、IP 变化,规则依然有效,且不依赖具体网段。

网络 ACL 的规则设计则要同时考虑两个方向。以允许公网访问子网内 Nginx 为例,入站需要允许 443(源 0.0.0.0/0),出站需要允许返回流量。由于客户端源端口是临时端口,通常放行 1024-65535 到 0.0.0.0/0。若实例还需主动访问外网,则入站也要放行 1024-65535 作为返回方向。规则编号建议按 100、110、120 递增,预留插入空间,避免后续调整时大面积重排。

安全组规则数量有配额限制(默认每 ENI 入站/出站各 60 条,可申请提升)。规则过多时应通过安全组引用和分组来收敛,而不是持续堆叠 CIDR。

三、分层安全策略

推荐的分层模型是:网络 ACL 做粗粒度子网边界控制,安全组做细粒度实例级控制,两者职责不重叠。NACL 适合做“整段子网只允许特定端口范围”的兜底,例如数据库子网 NACL 只允许 3306 入站、只允许来自应用子网 CIDR;安全组则精确到“哪个安全组的哪个端口”。

一个可参考的生产分层示例:

层级对象入站规则示例出站规则示例
子网边界公网子网 NACL100 允许 443 来自 0.0.0.0/0100 允许 1024-65535 到 0.0.0.0/0
实例级ALB 安全组443 来自 0.0.0.0/0默认全部允许
实例级应用安全组8080 来自 ALB 安全组3306 到数据库安全组
实例级数据库安全组3306 来自应用安全组默认全部允许

四、生产环境常见误配置与排错

误配一:NACL 只放行入站,忽略出站返回。现象是 curl 卡住直到超时,tcpdump 能看到 SYN 到达但看不到 SYN-ACK 发出。排查时先看 VPC Flow Logs 中该 ENI 的出站记录是否为 REJECT,再核对 NACL 出站规则是否覆盖临时端口范围。

误配二:安全组出站被收紧后忘记放行返回。安全组本身有状态,但如果出站规则被改成只允许特定目标,而应用需要访问外部 API,则连接无法建立。此时应检查出站规则是否包含目标端口,而不是怀疑返回流量。

误配三:NACL 规则编号顺序错误。例如先写了 100 拒绝某网段,再写 110 允许,结果允许规则永远不会被匹配。NACL 按编号从小到大匹配,命中即停止,拒绝规则必须放在允许规则之后,或使用更精确的编号规划。

误配四:安全组引用跨账户或跨 VPC 失效。安全组引用仅在同一 VPC 内有效(跨 VPC 需通过 VPC Peering 或 Transit Gateway 并配合相应规则)。跨 VPC 场景下应改用 CIDR 或前缀列表。

排错时建议按“从外到内、从下到上”的顺序:先用 Reachability Analyzer 判断路径是否可达,再查 VPC Flow Logs 定位是 NACL 还是 SG 拒绝,最后在实例上用 ss -tn 或 tcpdump 确认连接状态。以下命令可用于快速观察:

# 查看当前监听端口与连接状态
ss -tnlp | grep -E '443|8080|3306'

# 抓取 443 端口握手过程,确认 SYN / SYN-ACK 是否成对出现
sudo tcpdump -ni eth0 'tcp port 443 and (tcp[tcpflags] & (tcp-syn|tcp-ack) != 0)'

# 从应用实例测试到数据库的连通性
nc -vz -w 3 10.0.2.15 3306
修改 NACL 规则会立即影响子网内所有实例,且无状态特性意味着任何方向遗漏都会造成连接中断。生产环境调整前应先在测试子网验证,并保留回滚规则编号。

把安全组和网络 ACL 当成两层互补的过滤器:NACL 管“子网这道门”,安全组管“实例这扇窗”。有状态与无状态的差异决定了返回流量是否需要显式放行,而规则编号与引用方式决定了策略能否随架构演进保持可维护。掌握这两点,绝大多数“能 ping 通但服务不通”“握手成功但超时”的问题都能在几分钟内定位到具体规则。