诊断网络 ACL 的返回路径

AWSBeginner
立即练习

简介

一个公网配送应用已经具备互联网路由、公网地址和 HTTP 安全组授权,但客户端 A 仍然无法读取它。子网的网络 ACL 允许请求的目的端口,却阻断客户端的返回端口。你将诊断并修复这条路径,观察规则优先级,最后恢复预置的初始配置。

请先完成 Connect Privately to S3 with a VPC Endpoint。本次全新环境独立提供应用、公有和私有子网、公网路由、地址、安全组及自定义 ACL,CLI 也已配置。保留这些资源和参考网络。只修改本实验讲解的自定义 ACL 规则,并在清理时删除临时拒绝规则。最终基线会有意重现初始故障,不会删除预置应用。

认证关联

本实验为以下考试主题提供基础练习。

检查应用的子网边界

本步骤先定位应用,对比其路由、安全组和网络 ACL,再进行修改。

使用 Terminal,并在旁边打开 AWS View。公网应用的私有地址为 10.20.1.10;客户端 A 为 198.51.100.10,客户端 B 为 198.51.100.20。预置安全组只允许客户端 A 访问 TCP 80,未授权端口 8081。

cd /home/labex/project

根据 Name 标签选择 VPC。过滤器选择资源,query 提取 ID,$(...) 将结果保存供后续命令使用:

VPC_ID=$(aws ec2 describe-vpcs \
  --filters Name=tag:Name,Values=application-network \
  --query 'Vpcs[0].VpcId' \
  --output text)

保存公网应用子网及其预置网卡的 ID:

SUBNET_ID=$(aws ec2 describe-subnets \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=public-subnet \
  --query 'Subnets[0].SubnetId' \
  --output text)
ENI_ID=$(aws ec2 describe-network-interfaces \
  --filters "Name=subnet-id,Values=$SUBNET_ID" \
  --query 'NetworkInterfaces[0].NetworkInterfaceId' \
  --output text)

读取私有地址、公网地址关联和已附加安全组:

aws ec2 describe-network-interfaces \
  --network-interface-ids "$ENI_ID" \
  --query 'NetworkInterfaces[].{Private:PrivateIpAddress,Public:Association.PublicIp,Groups:Groups}' \
  --output json

读取与该子网关联的路由表:

aws ec2 describe-route-tables \
  --filters "Name=association.subnet-id,Values=$SUBNET_ID" \
  --query 'RouteTables[].{Routes:Routes,Associations:Associations}' \
  --output json

公网地址已关联,活动的 0.0.0.0/0 路由指向互联网网关。保存并读取预置安全组:

GROUP_ID=$(aws ec2 describe-security-groups \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=group-name,Values=supplied-application \
  --query 'SecurityGroups[0].GroupId' \
  --output text)
aws ec2 describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].{Inbound:IpPermissions,Outbound:IpPermissionsEgress}' \
  --output json

入站授权为来自 198.51.100.10/32 的 TCP 80,并具有预置的默认出站授权。保留这些规则。

网络 ACL 控制跨越子网边界的流量。每个子网只有一个 ACL,一个 ACL 可以服务多个子网。与有状态安全组不同,ACL 是无状态的:允许请求不会自动允许响应。选择与应用子网关联的 ACL:

ACL_ID=$(aws ec2 describe-network-acls \
  --filters "Name=association.subnet-id,Values=$SUBNET_ID" \
  --query 'NetworkAcls[0].NetworkAclId' \
  --output text)

读取其身份、关联及独立的入站和出站条目:

aws ec2 describe-network-acls \
  --network-acl-ids "$ACL_ID" \
  --query 'NetworkAcls[].{ID:NetworkAclId,Default:IsDefault,Associations:Associations,Entries:Entries}' \
  --output json

这是自定义 application-acl,不是默认 ACL。两个方向的规则 100 都允许客户端 A 的 TCP 目的端口 80。Egress: false 表示入站,true 表示出站;协议 6 是 TCP。未匹配流量最终被拒绝,该规则在 AWS View 中显示为 *,在 CLI 中显示为 32767。

在 AWS View 点击 Request application · client A:即使公网路由和安全组授权存在,结果仍为 Connection failed。Request application · client B 和 Request port 8081 也失败。保持此 Terminal 打开,以保留已保存的 ID。

修复客户端返回端口规则

本步骤替换错误的出站端口匹配,同时保留严格的客户端地址范围。

HTTP 请求从客户端选择的临时端口发往服务器端口 80;响应从服务器端口 80 发往该客户端端口。网络 ACL 在每个方向匹配数据包的目的端口,出站目的端口 80 规则不能放行这种响应。

临时端口范围由发起连接的客户端决定。本练习只向客户端 A 198.51.100.10/32 放行常见客户端范围 1024–65535;不要把它当作所有操作系统统一的默认值。替换现有出站规则 100;--egress 选择出站方向,--port-range 指定目的端口范围:

aws ec2 replace-network-acl-entry \
  --network-acl-id "$ACL_ID" \
  --rule-number 100 \
  --protocol 6 \
  --rule-action allow \
  --egress \
  --cidr-block 198.51.100.10/32 \
  --port-range From=1024,To=65535

替换成功时没有输出。读取条目:

aws ec2 describe-network-acls \
  --network-acl-ids "$ACL_ID" \
  --query 'NetworkAcls[].Entries' \
  --output json

入站规则 100 仍允许来自客户端 A 的 TCP 80。出站规则 100 现在允许发往同一客户端的目的端口 1024–65535。默认拒绝规则和子网关联保持原样。

在 AWS View 再次点击 Request application · client A。新请求成功返回 Application online,源地址为 198.51.100.10,目的端口为 80。Request application · client B 和 Request port 8081 仍失败。请求与响应现在都能跨越 ACL;此次修复没有扩大入站安全组或子网 ACL 对其他客户端的授权。

修复后的 ACL 放行客户端 A,并保留默认拒绝规则

示例:入站规则 100 允许客户端 A 的 TCP 80,出站规则 100 允许发往该客户端响应目的端口 1024–65535。两个方向均显示末尾默认拒绝,真实请求返回 Application online。你的环境中的资源 ID 可能不同。

观察较小编号的拒绝规则

本步骤有意在入站允许规则前插入匹配的拒绝规则,观察规则顺序如何影响访问。

每个方向的网络 ACL 规则都从最小编号开始评估。第一个匹配规则决定结果,后续规则不再评估。添加临时入站规则 90,拒绝客户端 A 的 TCP 80。--ingress 明确选择入站方向;创建规则 90 不会覆盖规则 100:

aws ec2 create-network-acl-entry \
  --network-acl-id "$ACL_ID" \
  --rule-number 90 \
  --protocol 6 \
  --rule-action deny \
  --ingress \
  --cidr-block 198.51.100.10/32 \
  --port-range From=80,To=80

读取条目:

aws ec2 describe-network-acls \
  --network-acl-ids "$ACL_ID" \
  --query 'NetworkAcls[].Entries' \
  --output json

入站拒绝 90 在入站允许 100 之前匹配。出站 100 仍放行客户端返回端口,安全组也仍允许客户端 A 的 HTTP。两者都不能覆盖更早匹配的 ACL 拒绝。

AWS View 显示入站规则 90 后,点击 Request application · client A,新请求会失败。客户端 B 和端口 8081 仍被阻断。先前成功的响应只是历史结果,修改规则后必须重新请求。保留规则 90 以完成本步骤检查,再在下一步删除它。

删除临时拒绝并重新检查访问

本步骤只删除较早的拒绝规则,验证修复后的返回路径仍然有效。

删除入站规则 90。方向很重要,因为入站和出站规则编号彼此独立:

aws ec2 delete-network-acl-entry --network-acl-id "$ACL_ID" --rule-number 90 --ingress

再次读取条目:

aws ec2 describe-network-acls \
  --network-acl-ids "$ACL_ID" \
  --query 'NetworkAcls[].Entries' \
  --output json

规则 90 已不存在。入站 100 允许客户端 A 的 TCP 80,出站 100 允许客户端返回端口,未匹配流量仍被拒绝。保持 ACL 与原子网的关联,保留安全组、公网路由和地址。

在 AWS View 发起新的 Request application · client A,会成功返回 Application online。Request application · client B 和 Request port 8081 仍失败。有状态安全组允许已放行请求的响应,但无状态子网 ACL 仍需要两个方向的授权;删除较早拒绝规则后,这个独立边界恢复通行。

恢复预置的初始 ACL 配置

本步骤撤销返回端口修改,确认没有临时拒绝规则,保留所有预置资源。

将出站规则 100 恢复为预置的目的端口 80。该清理有意恢复初始故障,这不是 HTTP 响应路径正常工作时推荐使用的规则:

aws ec2 replace-network-acl-entry \
  --network-acl-id "$ACL_ID" \
  --rule-number 100 \
  --protocol 6 \
  --rule-action allow \
  --egress \
  --cidr-block 198.51.100.10/32 \
  --port-range From=80,To=80

读取该 VPC 完整 ACL 清单及关联。不要仅凭可删除的标签证明清理完成:

aws ec2 describe-network-acls \
  --filters "Name=vpc-id,Values=$VPC_ID" \
  --query 'NetworkAcls[].{ID:NetworkAclId,Default:IsDefault,Associations:Associations,Entries:Entries}' \
  --output json

临时规则 90 必须不存在。预置自定义 ACL 仍属于公网应用子网。两个方向的规则 100 都匹配客户端 A 的 TCP 目的端口 80,末尾拒绝规则完整保留。私有子网仍使用默认 ACL。不要删除或替换预置 ACL、子网、应用、安全组、网关或公网地址。

读取原安全组,确认规则已保留:

aws ec2 describe-security-groups \
  --group-ids "$GROUP_ID" \
  --query 'SecurityGroups[].{Inbound:IpPermissions,Outbound:IpPermissionsEgress}' \
  --output json

在 AWS View 发起新的 Request application · client A,它会因返回端口不再被放行而再次失败。客户端 B 和端口 8081 仍被阻断。API 失败或应用网络不可用不能证明清理成功;认证后的资源清单查询必须成功,实际配置的路径也必须产生上述结果。

运行本步骤的完成检查。

总结

你定位了公网应用的子网 ACL,并诊断出缺失的响应端口范围。替换出站规则后,客户端 A 恢复真实 HTTP 访问,其他来源和端口仍被阻断。较小编号的入站拒绝随后展示了首次匹配的规则顺序;删除该规则后访问恢复。

你保留了预置安全组、路由、地址和子网关联,删除了临时拒绝,并恢复原始 ACL 基线。