VPC Network ACL
학습 목표
- Network ACL(NACL)의 역할과 보안 그룹과의 차이를 이해한다
- 이중 방어(Defense in Depth) 개념을 이해한다
- 퍼블릭/프라이빗 서브넷에 맞는 커스텀 NACL을 생성한다
- NACL의 Stateless 특성과 규칙 번호 순서 평가를 이해한다
왜 Network ACL이 필요한가?
Chapter 07에서 보안 그룹을 최적화하여 EC2와 RDS에 대한 접근을 제한했습니다. 보안 그룹만으로도 충분할까요? 아닙니다. 만약 누군가 보안 그룹을 실수로 0.0.0.0/0으로 열어버리면 어떻게 될까요? Network ACL(NACL)이 서브넷 레벨에서 한 번 더 막아줍니다.
보안 그룹은 각 방의 도어락과 같습니다. 방마다 출입 권한을 따로 설정합니다.
NACL은 건물 층별 출입 게이트와 같습니다. 해당 층 전체의 출입을 통제합니다.
도어락(보안 그룹)이 고장나거나 실수로 열려도, 층별 게이트(NACL)가 막아줍니다. 이것이 바로 이중 방어(Defense in Depth)입니다. 현업에서는 두 가지를 함께 사용하는 것이 표준입니다.
ShopEasy의 EC2 서버는 퍼블릭 서브넷에, RDS 데이터베이스는 프라이빗 서브넷에 위치합니다. 보안 그룹만으로 보호하고 있지만, 다음과 같은 상황을 생각해보세요:
- 팀원이 테스트를 위해 보안 그룹을
0.0.0.0/0으로 열고 되돌리는 것을 깜빡한다 - 새로운 EC2 인스턴스를 배포할 때 보안 그룹 설정을 빠뜨린다
- 자동화 스크립트 오류로 보안 그룹 규칙이 변경된다
NACL이 서브넷 레벨에서 추가 방어선을 제공하면, 이런 실수가 발생해도 피해를 최소화할 수 있습니다.
보안 그룹 vs NACL 비교
| 구분 | 보안 그룹 (Security Group) | Network ACL (NACL) |
|---|---|---|
| 적용 레벨 | 인스턴스(ENI) 레벨 | 서브넷 레벨 |
| 상태 | Stateful (상태 유지) | Stateless (상태 비저장) |
| 규칙 유형 | 허용(Allow) 규칙만 | 허용(Allow) + 거부(Deny) 규칙 |
| 규칙 평가 | 모든 규칙을 함께 평가 | 규칙 번호 순서대로 평가 |
| 기본 동작 | 모든 트래픽 거부 (규칙으로 허용) | 기본 NACL: 모든 트래픽 허용 |
| 적용 방식 | 인스턴스에 직접 연결 | 서브넷에 연결 (서브넷 내 모든 인스턴스에 적용) |
| 비유 | 각 방의 도어락 | 건물 층별 출입 게이트 |
보안 그룹 (Stateful): 인바운드로 허용된 트래픽의 응답은 아웃바운드 규칙에 관계없이 자동으로 허용됩니다. 즉, 요청이 들어오면 응답은 자동으로 나갑니다.
NACL (Stateless): 인바운드를 허용해도 응답 트래픽(아웃바운드)은 자동으로 허용되지 않습니다. 반드시 인바운드와 아웃바운드 양쪽 모두 규칙을 설정해야 합니다. 이것이 NACL 설정에서 가장 많이 실수하는 부분입니다!
NACL은 규칙을 번호가 낮은 것부터 순서대로 평가합니다. 일치하는 규칙을 찾으면 즉시 적용하고 나머지 규칙은 무시합니다.
예를 들어, 규칙 100이 "SSH 허용"이고 규칙 200이 "SSH 거부"라면, 규칙 100이 먼저 평가되어 SSH가 허용됩니다. 반대로 규칙 100이 "SSH 거부"이고 규칙 200이 "SSH 허용"이면, SSH가 거부됩니다.
*(별표) 규칙은 가장 마지막에 평가되며, 위의 어떤 규칙에도 일치하지 않는 모든 트래픽을 거부합니다.
출입 게이트
도어락
실습: VPC Network ACL 구성
Network ACL은 추가 비용이 발생하지 않습니다. VPC의 기본 기능으로 무료로 사용할 수 있습니다.
-
Step 1: 현재 기본 NACL 상태 확인
VPC를 생성하면 기본 NACL(Default NACL)이 자동으로 생성됩니다. 기본 NACL은 모든 인바운드/아웃바운드 트래픽을 허용합니다. 먼저 이 상태를 확인해보겠습니다.
확인 사항
- ShopEasy-VPC에 연결된 기본 NACL 찾기
- 인바운드 규칙: 규칙 100이 "ALL Traffic - Allow"인지 확인
- 아웃바운드 규칙: 규칙 100이 "ALL Traffic - Allow"인지 확인
- 연결된 서브넷 목록 확인 (4개 서브넷 모두 기본 NACL에 연결되어 있을 것)
기본 NACL이 위험한 이유기본 NACL은 모든 트래픽을 허용하므로, NACL로서의 방어 역할을 전혀 하지 못합니다. 이것은 건물의 출입 게이트가 항상 열려 있는 것과 같습니다. 보안 그룹에만 의존하게 되어, 보안 그룹 설정 실수 시 바로 취약해집니다.
NACL 확인 위치AWS 콘솔 → VPC → 왼쪽 메뉴의 Network ACLs에서 확인할 수 있습니다. Default 열이 "Yes"인 것이 기본 NACL입니다.
AWS 콘솔 → VPC 서비스 → 왼쪽 메뉴 Network ACLs 클릭 → ShopEasy-VPC에 속한 NACL을 찾습니다 (VPC 열에서 ShopEasy-VPC 확인).
기본 NACL을 선택하고 Inbound rules 탭과 Outbound rules 탭에서 규칙을 확인합니다.
Subnet associations 탭에서 연결된 서브넷 목록을 확인합니다.
-
Step 2: 퍼블릭 서브넷용 커스텀 NACL 생성
퍼블릭 서브넷(ShopEasy-Public-A, ShopEasy-Public-C)에 적용할 커스텀 NACL을 생성합니다. EC2 서버에 필요한 SSH(22), API(5000), HTTPS(443), 임시 포트만 허용합니다.
생성 정보
- 이름:
ShopEasy-Public-NACL - VPC: ShopEasy-VPC
인바운드 규칙 설정
규칙 # 유형 프로토콜 포트 소스 허용/거부 100 SSH TCP 22 0.0.0.0/0 Allow 110 사용자 지정 TCP TCP 5000 0.0.0.0/0 Allow 120 HTTPS TCP 443 0.0.0.0/0 Allow 130 사용자 지정 TCP TCP 1024-65535 0.0.0.0/0 Allow * 모든 트래픽 전체 전체 0.0.0.0/0 Deny 아웃바운드 규칙 설정
규칙 # 유형 프로토콜 포트 대상 허용/거부 100 모든 트래픽 전체 전체 0.0.0.0/0 Allow * 모든 트래픽 전체 전체 0.0.0.0/0 Deny 핵심 개념: 임시 포트(Ephemeral Ports)가 필요한 이유NACL은 Stateless이므로 인바운드를 허용해도 응답 트래픽(아웃바운드)은 자동으로 허용되지 않습니다. 반드시 양방향 모두 규칙을 설정해야 합니다.
규칙 130의 임시 포트(1024-65535)는 서버가 클라이언트에게 응답을 보낼 때 사용하는 포트입니다. 클라이언트(브라우저 등)가 서버에 요청을 보내면, 서버의 응답은 클라이언트의 임시 포트로 돌아갑니다. 이 규칙이 없으면 외부에서 오는 응답 트래픽이 차단됩니다.
규칙 120의 HTTPS(443)는 왜 필요한가?EC2에서
yum update,npm install등 패키지를 설치할 때 인터넷(HTTPS)에 접근해야 합니다. 또한 EC2에서 S3, DynamoDB 등 AWS 서비스에 접근할 때도 HTTPS를 사용합니다. 이 규칙이 없으면 EC2에서 소프트웨어를 설치하거나 AWS 서비스에 접근할 수 없게 됩니다.커스텀 NACL 생성 시 주의새로 생성한 커스텀 NACL은 기본적으로 모든 트래픽을 거부합니다 (기본 NACL과 반대). 인바운드/아웃바운드 규칙을 추가하기 전에 서브넷에 연결하면 모든 트래픽이 차단됩니다. 반드시 규칙을 먼저 설정한 후 서브넷에 연결하세요!
VPC 콘솔 → Network ACLs → Create network ACL 클릭
- Name:
ShopEasy-Public-NACL입력 - VPC:
ShopEasy-VPC선택 - Create network ACL 클릭
- 생성된 NACL 선택 → Inbound rules 탭 → Edit inbound rules 클릭
- Add new rule을 클릭하여 규칙 4개를 추가 (규칙 번호 100, 110, 120, 130)
- Save changes 클릭
- Outbound rules 탭 → Edit outbound rules 클릭
- 규칙 100을 추가 (모든 트래픽, Allow)
- Save changes 클릭
- 이름:
-
Step 3: 프라이빗 서브넷용 커스텀 NACL 생성
프라이빗 서브넷(ShopEasy-Private-A, ShopEasy-Private-C)에 적용할 커스텀 NACL을 생성합니다. 프라이빗 서브넷에는 RDS가 위치하므로, 퍼블릭 서브넷에서 오는 MySQL(3306) 트래픽만 허용합니다.
생성 정보
- 이름:
ShopEasy-Private-NACL - VPC: ShopEasy-VPC
인바운드 규칙 설정
규칙 # 유형 프로토콜 포트 소스 허용/거부 100 MySQL/Aurora TCP 3306 10.0.1.0/24 Allow 110 MySQL/Aurora TCP 3306 10.0.2.0/24 Allow 120 사용자 지정 TCP TCP 1024-65535 0.0.0.0/0 Allow * 모든 트래픽 전체 전체 0.0.0.0/0 Deny 아웃바운드 규칙 설정
규칙 # 유형 프로토콜 포트 대상 허용/거부 100 모든 트래픽 전체 전체 0.0.0.0/0 Allow * 모든 트래픽 전체 전체 0.0.0.0/0 Deny 프라이빗 NACL의 소스가 서브넷 CIDR인 이유프라이빗 서브넷의 NACL은 퍼블릭 서브넷(
10.0.1.0/24,10.0.2.0/24)에서 오는 MySQL 트래픽만 허용합니다. 인터넷에서 직접 프라이빗 서브넷에 접근하는 것은 라우팅에서도 불가능하지만, NACL로 한 번 더 보호합니다.규칙이 2개인 이유는 ShopEasy-Public-A(
10.0.1.0/24)와 ShopEasy-Public-C(10.0.2.0/24) 두 퍼블릭 서브넷 모두에서 RDS에 접근할 수 있어야 하기 때문입니다.임시 포트(1024-65535) 규칙의 역할프라이빗 서브넷의 인바운드 규칙 120은 NAT Gateway를 통해 인터넷에서 돌아오는 응답 트래픽을 허용합니다. 예를 들어, RDS 인스턴스가 보안 패치를 다운로드할 때 NAT Gateway를 통해 인터넷에 접속하고, 그 응답이 임시 포트로 돌아옵니다.
VPC 콘솔 → Network ACLs → Create network ACL 클릭
- Name:
ShopEasy-Private-NACL입력 - VPC:
ShopEasy-VPC선택 - Create network ACL 클릭
- 생성된 NACL 선택 → Inbound rules 탭 → Edit inbound rules 클릭
- Add new rule을 클릭하여 규칙 3개를 추가:
- 규칙 100: Type = MySQL/Aurora(3306), Source =
10.0.1.0/24, Allow - 규칙 110: Type = MySQL/Aurora(3306), Source =
10.0.2.0/24, Allow - 규칙 120: Type = Custom TCP, Port Range =
1024-65535, Source =0.0.0.0/0, Allow
- 규칙 100: Type = MySQL/Aurora(3306), Source =
- Save changes 클릭
- Outbound rules 탭 → Edit outbound rules 클릭
- 규칙 100: Type = All traffic, Destination =
0.0.0.0/0, Allow - Save changes 클릭
- 이름:
-
Step 4: NACL을 서브넷에 연결하고 테스트
생성한 커스텀 NACL을 각 서브넷에 연결합니다. 연결하는 순간 해당 서브넷의 트래픽 제어가 기본 NACL에서 커스텀 NACL로 전환됩니다.
연결 정보
- ShopEasy-Public-NACL → ShopEasy-Public-A, ShopEasy-Public-C
- ShopEasy-Private-NACL → ShopEasy-Private-A, ShopEasy-Private-C
연결 후 테스트
- SSH 접속 테스트: EC2에 SSH로 정상 접속되는지 확인
- API 접속 테스트: 브라우저에서
http://EC2_PUBLIC_IP:5000/api/products접속 확인 - RDS 접속 테스트: EC2에서
mysql -h RDS_ENDPOINT -u admin -p명령으로 데이터베이스 접속 확인
NACL 설정 오류 시 대처 방법NACL을 잘못 설정하면 EC2에 SSH 접속이 불가능해질 수 있습니다! SSH가 안 되면 당황하지 마세요.
해결 방법: AWS 웹 콘솔은 NACL과 무관하게 항상 접속 가능합니다. VPC → Network ACLs에서 문제가 있는 NACL의 서브넷 연결을 해제하고 기본 NACL로 되돌리면 됩니다. 또는 NACL의 인바운드 규칙을 수정하여 문제를 해결합니다.
서브넷 연결 방법NACL 선택 → Subnet associations 탭 → Edit subnet associations를 클릭하여 서브넷을 선택합니다. 하나의 서브넷은 하나의 NACL에만 연결될 수 있습니다. 새 NACL에 연결하면 기존 NACL(기본 NACL)에서 자동으로 해제됩니다.
퍼블릭 서브넷 연결:
- VPC → Network ACLs →
ShopEasy-Public-NACL선택 - Subnet associations 탭 → Edit subnet associations 클릭
ShopEasy-Public-A와ShopEasy-Public-C를 체크- Save changes 클릭
프라이빗 서브넷 연결:
ShopEasy-Private-NACL선택- Subnet associations 탭 → Edit subnet associations 클릭
ShopEasy-Private-A와ShopEasy-Private-C를 체크- Save changes 클릭
테스트:
- 터미널에서 SSH 접속:
ssh -i "ShopEasy-Key.pem" ec2-user@EC2_PUBLIC_IP - 브라우저에서 API 접속:
http://EC2_PUBLIC_IP:5000/api/products - EC2 내에서 RDS 접속:
mysql -h RDS_ENDPOINT -u admin -p
확인 사항
- 기본 NACL의 규칙이 "모든 트래픽 허용"임을 확인했다
- ShopEasy-Public-NACL이 생성되고 인바운드/아웃바운드 규칙이 설정되었다
- ShopEasy-Private-NACL이 생성되고 인바운드/아웃바운드 규칙이 설정되었다
- 퍼블릭 서브넷 2개(Public-A, Public-C)에 Public NACL이 연결되었다
- 프라이빗 서브넷 2개(Private-A, Private-C)에 Private NACL이 연결되었다
- SSH 접속이 정상 동작한다
- API(포트 5000) 접속이 정상 동작한다
- RDS(포트 3306) 접속이 정상 동작한다 (EC2에서 mysql 명령)
- Network ACL의 역할과 보안 그룹과의 차이를 이해
- 이중 방어(Defense in Depth) 전략을 VPC에 적용
- 퍼블릭 서브넷에 필요한 포트만 허용하는 커스텀 NACL 구성
- 프라이빗 서브넷에 퍼블릭 서브넷 CIDR에서만 MySQL 접근을 허용하는 NACL 구성
- Stateless 특성을 고려한 양방향 규칙 설정
NACL로 서브넷 레벨의 방화벽을 구성했습니다. 다음 챕터에서는 VPC Flow Logs를 활성화하여 VPC 내의 네트워크 트래픽을 기록하고 모니터링합니다. NACL과 보안 그룹에서 허용/거부된 트래픽을 로그로 확인할 수 있어, 보안 사고 분석과 문제 해결에 활용됩니다.