소개
공개 배송 애플리케이션에는 인터넷 경로, 공개 주소, HTTP 보안 그룹 허용이 있지만 클라이언트 A가 읽을 수 없습니다. 서브넷 ACL은 요청의 목적지 포트를 허용하면서 클라이언트 반환 포트를 차단합니다. 이 경로를 진단하고 복구한 뒤 규칙 우선순위를 관찰하고 제공된 초기 구성을 복원합니다.
먼저 Connect Privately to S3 with a VPC Endpoint를 완료하세요. 새 환경은 자체 애플리케이션, 공개·비공개 서브넷, 공개 경로, 주소, 그룹, 사용자 지정 ACL을 제공합니다. CLI는 구성되어 있습니다. 이 리소스와 참조 네트워크를 보존하세요. 여기서 다루는 ACL 규칙만 변경하고 정리할 때 임시 거부를 삭제하세요. 최종 기준 상태는 초기 장애를 의도적으로 재현하며 제공된 애플리케이션을 삭제하지 않습니다.
자격시험 관련성
다음 시험 주제를 위한 기초 실습입니다.
- Cloud Practitioner (CLF-C02) · 과제 3.5: VPC 보안 그룹과 네트워크 ACL의 기본 역할.
- Solutions Architect – Associate (SAA-C03) · 과제 1.2: 서브넷 보안 제어와 애플리케이션 접근 조건.
- CloudOps Engineer – Associate (SOA-C03) · 과제 5.3: ACL 규칙과 응답 포트를 통한 연결 진단.
- Advanced Networking – Specialty (ANS-C01) · 과제 3.1: 순서가 있는 서브넷 규칙 유지와 실제 반환 경로 확인의 기초.
애플리케이션의 서브넷 경계 조사
변경 전에 애플리케이션을 찾고 경로, 보안 그룹, 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)
공개 서브넷과 제공된 인터페이스를 저장합니다.
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
이는 기본 ACL이 아닌 사용자 지정 application-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도 실패합니다. 저장한 ID를 유지하려면 이 Terminal을 열어 두세요.
클라이언트 반환 포트 규칙 복구
좁은 클라이언트 주소 범위를 유지하며 잘못된 아웃바운드 포트를 교체합니다.
HTTP 요청은 클라이언트가 선택한 임시 포트에서 서버 80으로 갑니다. 응답은 서버 80에서 해당 클라이언트 포트로 갑니다. ACL은 방향별로 패킷의 목적지 포트를 비교하므로 아웃바운드 목적지 80은 이 응답을 허용하지 못합니다.
임시 범위는 연결을 시작하는 클라이언트에 따라 다릅니다. 여기서는 일반적인 1024–65535를 A 198.51.100.10/32 방향에만 허용합니다. 모든 OS의 동일한 기본값으로 생각하지 마세요. 기존 아웃바운드 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 허용을 넓히지 않고 요청과 응답 모두 통과합니다.

예: 인바운드 100은 A TCP 80, 아웃바운드 100은 A로 돌아가는 응답 목적지 1024–65535를 허용합니다. 양방향의 최종 거부가 표시되고 실제 요청은 Application online을 반환합니다. 리소스 ID는 다를 수 있습니다.
더 작은 번호의 거부 규칙 관찰
규칙 순서를 관찰하려고 인바운드 허용보다 앞에 일치하는 거부를 의도적으로 넣습니다.
방향별 ACL 규칙은 작은 번호부터 평가됩니다. 첫 일치가 결과를 결정하며 후속 규칙은 평가하지 않습니다. A TCP 80을 거부하는 임시 인바운드 90을 만듭니다. --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은 반환 포트를 허용하고 불일치 트래픽은 거부합니다. 원래 서브넷 연결, 그룹, 공개 경로와 주소를 보존합니다.
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 기준으로 돌아갔습니다.



