
[security] 보안 그룹 & NACL 심화
stateful이 실제로 뭘 뜻하는지, 3-티어에선 어떻게 쓰는지. 보안 그룹과 NACL을 실무 감각으로 깊게 팝니다.
이 노트는 이론 심화 + AWS 실제 활용 사례 중심이다. 시험 직전 빠른 비교/트리거는 network/security-group-vs-nacl.md를 함께 본다.
One-Line Summary
Security Group(SG)과 Network ACL(NACL)은 VPC 안에서 트래픽을 막는 두 개의 방화벽 계층이다. SG는 ENI(인스턴스) 단위·stateful·allow-only, NACL은 서브넷 단위·stateless·allow+deny다. 실무에서는 SG를 주력 접근 제어로 쓰고(계층 간에는 IP가 아니라 SG를 서로 참조), NACL은 서브넷 경계의 보조 방어선(특정 IP/대역 차단, 규정용 격리)으로 쓴다. 이 역할 분담과 "왜 그렇게 쓰는가"를 이해하는 게 핵심이다.
Why It Exists — 심층 방어(Defense in Depth) 안에서의 위치
AWS 네트워크 보안은 하나의 장치가 아니라 여러 겹으로 이루어진다. SG/NACL은 그중 VPC 계층의 두 겹이다.
사용자 요청
│
▼
[Route 53 / CloudFront / WAF / Shield] ← 엣지: L7 필터·DDoS 완화(별도 노트)
│
▼
[Internet Gateway] → [Route Table] ← 경로가 있어야 도달 (라우팅은 방화벽 아님)
│
▼
[Network ACL] ← 서브넷 경계, stateless, allow/deny ◀── 이 노트
│
▼
[Security Group] ← ENI(인스턴스) 앞, stateful, allow만 ◀── 이 노트
│
▼
[호스트 OS 방화벽 / IAM / 앱 인증] ← 인스턴스 내부·권한 계층
핵심 사고방식:
라우트 테이블은 "길을 열고", SG/NACL은 "문을 지킨다". 통신이 안 되면 라우팅→NACL→SG를 순서대로 의심한다. 어느 한 겹이라도 막으면 통신은 실패한다(= 심층 방어).
이 두 겹을 나눈 이유:
- SG (인스턴스 관점): "이 서버에 누가/무엇이 접근할 수 있나"를 리소스별로 정밀 제어.
- NACL (서브넷 관점): "이 네트워크 구획 전체에 무엇을 들이고 내보낼까"를 광역으로 제어 — 개별 SG가 잘못 설정돼도 막아주는 안전망.
Abbreviations
| 약어 | 풀네임 | 의미 |
|---|---|---|
SG | Security Group | ENI 단위 stateful 방화벽 |
NACL | Network Access Control List | 서브넷 단위 stateless 방화벽 |
ENI | Elastic Network Interface | 인스턴스·Lambda·엔드포인트에 붙는 가상 NIC (SG가 여기 적용) |
Stateful | Stateful | 허용된 요청의 응답을 연결 추적으로 자동 허용 |
Stateless | Stateless | 요청·응답을 각각 규칙으로 명시해야 함 |
Ephemeral Port | Ephemeral Port | 클라이언트가 응답 수신용으로 임시로 여는 높은 번호 포트 |
5-tuple | 5-tuple | (출발지 IP·포트, 목적지 IP·포트, 프로토콜) — 연결을 식별하는 5요소 |
Part 1. 이론 심화
1-1. 패킷은 실제로 어떻게 검사되나
인바운드 패킷 하나가 EC2에 도달하기까지, 그리고 응답이 나가기까지의 검사 지점:
인바운드: 외부 ─▶ [NACL 인바운드 규칙] ─▶ [SG 인바운드 규칙] ─▶ ENI ─▶ EC2
아웃바운드(응답): EC2 ─▶ [SG] ─▶ [NACL 아웃바운드 규칙] ─▶ 외부
- NACL이 먼저(서브넷 경계) → 그다음 SG(ENI).
- SG는 stateful이라 응답에 대해 규칙을 안 봐도 되지만, NACL은 stateless라 응답이 나가는 아웃바운드도 규칙을 통과해야 한다.
- 같은 서브넷 내부 인스턴스끼리 통신하면 NACL은 개입하지 않는다(서브넷 경계를 넘지 않으므로). 이때는 SG만 적용된다 → "같은 서브넷인데 왜 안 되지?"는 대개 SG 문제.
1-2. Stateful의 실체 — 연결 추적(Connection Tracking)
SG가 "응답을 자동 허용"하는 건 마법이 아니라 연결 추적 테이블 덕분이다. AWS는 각 연결을 5-tuple로 기록해 두고, 그 연결에 속한 응답 패킷을 인식해 통과시킨다.
클라이언트 1.2.3.4:53820 ──TCP 80──▶ 웹서버 10.0.1.10:80
→ SG가 이 흐름을 연결 추적 테이블에 등록
→ 응답 10.0.1.10:80 ─▶ 1.2.3.4:53820 은 "알고 있는 연결의 응답"이라 자동 통과
(아웃바운드 규칙을 보지 않음)
실무 참고: SG 규칙을 삭제·변경해도 이미 추적 중인 기존 연결은 바로 끊기지 않고 타임아웃될 때까지 유지될 수 있다. "규칙을 지웠는데 왜 아직 붙어 있지?"의 원인.
1-3. Stateless와 Ephemeral 포트 — NACL 최대 함정
NACL은 연결 추적이 없다. 요청과 응답을 각각 허용해야 한다. 문제는 응답이 돌아가는 포트가 80이 아니라 클라이언트가 임시로 연 높은 번호 포트라는 것이다.
클라이언트 :53820 ──▶ 서버 :80 (요청: NACL 인바운드 80 허용 필요)
서버 :80 ──▶ 클라이언트 :53820 (응답: NACL 아웃바운드 1024-65535 허용 필요)
▲ 이 ephemeral 대역을 빠뜨리면 통신 절단
ephemeral 포트 범위는 클라이언트 종류마다 다르다 — 그래서 실무/시험 모두 넉넉히 1024–65535를 연다:
| 클라이언트 | 대표 ephemeral 범위 |
|---|---|
| Linux 커널 | 32768–60999 |
| Windows (최신) | 49152–65535 |
| NAT Gateway | 1024–65535 |
| ELB (ALB/NLB) | 1024–65535 |
그래서 커스텀 NACL에서는 아웃바운드(또는 응답 방향)에 TCP 1024–65535 allow를 관례적으로 넣는다.
1-4. 규칙 평가 방식의 차이
| Security Group | Network ACL | |
|---|---|---|
| 평가 | 모든 규칙을 종합(OR), 하나라도 맞으면 허용 | 번호 오름차순, 처음 매칭된 규칙을 즉시 적용(뒤는 무시) |
| deny | 없음(암묵적 거부만) | 명시적 deny 가능 |
| 특정 IP 차단 | 불가(allow-list라서) | 가능(그 IP만 deny 규칙) |
NACL 예 (번호 낮은 게 우선):
90 DENY 1.2.3.4/32 ← 먼저 평가 → 이 IP는 여기서 차단
100 ALLOW 0.0.0.0/0 :443 ← 나머지는 통과
SG는 "허용 목록만 관리". 악성 IP 하나를 차단하는 건 SG로 원천적으로 불가능 → NACL deny의 존재 이유.
Part 2. AWS 실제 활용 사례 (핵심)
사례 1. 3-티어 웹 애플리케이션 — SG 체이닝(SG가 SG를 참조)
가장 정석적이고 시험·실무 모두 단골인 패턴. IP를 쓰지 않고 SG를 서로 참조해 계층 간 접근을 논리적으로 연결한다.
[인터넷] ─443─▶ [ALB] (sg-alb)
│
▼ (ALB가 연결 종료 후 백엔드로 새 연결)
[Web/App EC2] (sg-app) ← Auto Scaling으로 IP 계속 바뀜
│
▼
[RDS] (sg-db)
각 SG의 인바운드 규칙(소스가 IP가 아니라 다른 SG인 점이 핵심):
| SG | 인바운드 규칙 | 의미 |
|---|---|---|
sg-alb | TCP 443 from 0.0.0.0/0 | 인터넷에서 HTTPS |
sg-app | TCP 80 from sg-alb | ALB SG를 가진 것만 앱에 접근 |
sg-db | TCP 3306 from sg-app | 앱 SG를 가진 것만 DB 3306 접근 |
왜 이렇게 하나 (실무 이유):
- Auto Scaling으로 앱 인스턴스 IP가 수시로 바뀌어도 규칙을 고칠 필요가 없다 — "sg-app을 가진 것"이라는 논리적 조건이라서.
- 최소 권한: DB는 인터넷은 물론 ALB에서도 직접 못 오고, 오직 앱 계층에서만.
- ALB는 연결을 종료하고 백엔드로 자기 이름으로 새 연결을 맺으므로 백엔드 SG는 ALB의 SG를 소스로 참조하면 된다.
시험 트리거: "allow only the application tier to access the database", "instances scale and IPs change" → SG가 SG를 참조.
사례 2. 관리 접근 — Bastion에서 SSM Session Manager로 (현대적 실무)
과거 방식 (Bastion Host):
운영자 ─22─▶ [Bastion EC2] (sg-bastion: 22 from 회사 IP만) ─22─▶ 프라이빗 EC2 (sg-app: 22 from sg-bastion)
- 퍼블릭 서브넷에 점프박스를 두고, SSH는 회사 고정 IP에서만, 내부 인스턴스는 bastion SG에서만 22 허용.
- 절대 하면 안 되는 것:
22 from 0.0.0.0/0(전 세계에 SSH 개방 — 가장 흔한 사고).
현대 권장 방식 (SSM Session Manager):
운영자 ─▶ AWS Systems Manager ─▶ (SSM Agent) 프라이빗 EC2
인바운드 22 규칙: 없음. 퍼블릭 IP: 없음. Bastion: 없음.
- 인바운드 SSH 포트를 아예 열지 않고, 아웃바운드로 SSM에 연결해 셸을 얻는다.
- SG 관점의 이점: 인바운드 공격 표면 = 0. 접근 통제는 IAM으로, 감사는 CloudTrail/세션 로그로.
시험/실무 감각: "SSH 포트를 열지 않고 EC2에 안전하게 접근" → SSM Session Manager(SG 인바운드 불필요).
사례 3. 공격 중 특정 IP/대역 차단 — NACL deny (SG로는 불가)
DDoS나 스캐닝의 출처 IP를 즉시 막아야 할 때. SG는 allow-list라 차단 규칙 자체가 없다. 서브넷 경계의 NACL에서 deny한다.
공격 대역 203.0.113.0/24 유입
→ 해당 서브넷 NACL:
80 DENY 203.0.113.0/24 (낮은 번호로 먼저 평가)
100 ALLOW 0.0.0.0/0 :443
- 서브넷 전체 리소스에 일괄 적용된다는 점이 광역 차단에 유리.
- 단, L7(HTTP 페이로드·SQLi 등) 기반 차단은 NACL로 못 함 → AWS WAF, 대규모 볼류메트릭 DDoS는 Shield/Network Firewall로 올라간다(다른 노트).
시험 트리거: "block a specific malicious IP range" → NACL deny.
사례 4. 서브넷 경계를 규정 준수 격리선으로 사용 — NACL 안전망
프라이빗 DB 서브넷은 애초에 인터넷과 통신할 이유가 없다. 개별 DB SG가 실수로 넓게 열려도, 서브넷 NACL이 인터넷 대역을 막아두면 사고를 예방한다.
DB 서브넷 NACL:
인바운드 ALLOW 10.0.0.0/16(VPC 내부) 만
아웃바운드 ALLOW 10.0.0.0/16 + ephemeral
→ 인터넷(0.0.0.0/0)과의 통신은 원천 차단 (SG 실수와 무관하게)
- 이것이 "SG가 주력, NACL은 안전망"이라는 실무 분담의 전형. NACL은 광역·거친 규칙으로 두고, 세밀 제어는 SG에 맡긴다.
사례 5. ALB vs NLB 뒤 백엔드의 SG (자주 틀리는 실무 포인트)
로드밸런서 종류에 따라 백엔드 인스턴스 SG가 무엇을 허용해야 하는지가 달라진다.
| LB 종류 | 백엔드에서 보이는 출발지 | 백엔드 SG 인바운드 |
|---|---|---|
| ALB | ALB (연결을 종료·재생성) | from sg-alb (LB의 SG 참조) |
| NLB (기본) | 클라이언트 원본 IP 보존 | 클라이언트 CIDR를 허용해야 함(LB가 아니라!) |
| NLB + SG 기능 | (SG를 붙이면) NLB SG 참조 가능 | from sg-nlb |
- 함정: NLB 뒤 인스턴스 SG에 "NLB만 허용"이라고 넣으면 안 통한다. NLB는 기본적으로 클라이언트 IP를 그대로 전달하므로 백엔드 SG는 클라이언트 IP 대역을 허용해야 한다. (NLB에 SG를 붙이는 기능을 쓰면 SG 참조도 가능.)
사례 6. Self-referencing SG — 클러스터 내부 통신
같은 역할의 노드들이 서로 통신해야 할 때(예: EKS 노드, ElastiCache/DB 클러스터, 분산 앱), SG가 자기 자신을 소스로 참조한다.
sg-cluster 인바운드:
All traffic from sg-cluster ← "같은 SG를 가진 멤버끼리는 서로 허용"
- 노드가 늘거나 IP가 바뀌어도 규칙 변경 불필요. VPC 기본 SG도 이 방식(자기 참조)으로 내부 통신을 허용한다.
사례 7. SG는 EC2만의 것이 아니다
SG는 ENI를 갖는 모든 리소스에 적용된다 — 실무에서 자주 잊는 지점.
| 리소스 | SG 적용 방식 |
|---|---|
| RDS/Aurora | DB에 SG 지정(앱 SG만 3306/5432 허용) |
| Lambda (VPC 연결) | 함수가 ENI를 얻고 SG 적용 → 프라이빗 RDS 접근 |
| Interface VPC Endpoint | 엔드포인트 ENI의 SG로 어떤 SG가 이 엔드포인트를 쓸지 통제 |
| ELB (ALB, NLB 옵션) | LB 자체에 SG |
| ElastiCache | 노드에 SG |
Part 3. 실무 운영·거버넌스
3-1. 흔한 실수(Anti-patterns)와 교정
| 안티패턴 | 왜 위험한가 | 올바른 방식 |
|---|---|---|
SSH 22 / RDP 3389 from 0.0.0.0/0 | 전 세계에 관리 포트 개방 | 회사 IP만, 또는 SSM Session Manager |
| 계층 간 접근에 IP 하드코딩 | 오토스케일로 IP 변동 시 깨짐 | SG 참조 |
| 모든 걸 NACL로 세밀 제어하려 함 | stateless·번호관리로 복잡·실수↑ | 세밀제어는 SG, NACL은 광역 안전망 |
| 커스텀 NACL 만들고 규칙 미기입 | 커스텀 NACL 기본은 전체 거부 → 전면 장애 | 명시 allow(+ephemeral) 규칙 추가 |
| 통신 장애를 SG만 확인 | 라우팅·NACL·SG 다층 | 세 겹을 함께 점검 |
| 규칙을 너무 넓게(all traffic) | 연결 추적이 untracked화, 감사 어려움 | 필요한 포트/소스만 좁게 |
3-2. 가시성·감사
| 목적 | 도구 |
|---|---|
| 실제 허용/거부된 트래픽 관찰 | VPC Flow Logs (ACCEPT/REJECT 기록) |
| "0.0.0.0/0 SSH 열린 SG 탐지" 자동화 | AWS Config 규칙(restricted-ssh 등) |
| 계정 전반 보안 상태 종합 | Security Hub |
Flow Logs에서 REJECT가 보이면 SG 또는 NACL이 막은 것 → 어느 계층인지 좁혀 진단.
Common Wrong Directions
| 오답 방향 | 왜 부족한가 |
|---|---|
| SG로 특정 악성 IP 차단 | SG는 allow-only → 차단은 NACL deny |
| NACL이 응답을 자동 허용할 거라 가정 | NACL은 stateless → ephemeral 아웃바운드를 직접 열어야 |
| NLB 뒤 SG에 "NLB만 허용" | NLB는 클라이언트 IP 보존 → 클라이언트 CIDR 허용 필요 |
| 관리 접근을 위해 SSH를 0.0.0.0/0 개방 | 공격 표면 폭증 → 회사 IP 한정 또는 SSM Session Manager |
| 계층 접근에 IP 하드코딩 | 오토스케일 IP 변동 → SG 참조 |
| DB 보호를 SG만 신뢰 | 서브넷 NACL 안전망으로 심층 방어 |
| 같은 서브넷 통신 실패를 NACL 탓 | 서브넷 내부는 NACL 미개입 → SG 확인 |
Exam Triggers
| 문제에서 이런 표현이 나오면 | 떠올릴 것 |
|---|---|
| "allow only app tier to reach DB / IPs change with scaling" | SG가 SG 참조 |
| "block a specific IP/CIDR" | NACL deny |
| "return/response traffic blocked" | NACL ephemeral 포트 allow |
| "access EC2 without opening inbound SSH / no bastion" | SSM Session Manager |
| "instances behind NLB can't be reached; SG references NLB" | 백엔드 SG가 클라이언트 CIDR 허용 |
| "subnet-wide policy / compliance isolation" | NACL |
| "instance-level, stateful firewall" | SG |
| "detect security groups open to the world" | AWS Config 규칙 |
| "see which traffic was accepted/rejected" | VPC Flow Logs |
Memory Sentence
SG는 ENI 단위·stateful(연결 추적)·allow-only라 응답이 자동 허용되고 특정 IP는 못 막는다. NACL은 서브넷 단위·stateless·allow+deny라 악성 IP를 막을 수 있지만 ephemeral 포트를 직접 열어야 한다. 실무는 SG를 주력(계층 간 SG 참조)으로, NACL을 서브넷 안전망(광역 차단·규정 격리)으로 쓴다. 관리 접근은 SSH 개방 대신 SSM, NLB 뒤 SG는 클라이언트 IP를 허용, 가시성은 Flow Logs·감사는 Config.
References
댓글
아직 댓글이 없습니다.