DB 포트를 세상에 열어 둔 채 '설마 누가 알겠어'라고 했다가
AWS 콘솔에서 보안 그룹을 급하게 만들면서 인바운드 규칙을 0.0.0.0/0:3306 ALLOW로 설정했다. "잠깐만"이라고 생각했지만 그 상태가 몇 주간 방치됐다. 어느 날 MySQL 슬로 쿼리 로그에서 처음 보는 IP가 대량으로 테이블 스캔을 실행한 흔적이 발견됐다.
방화벽 설정은 한 번에 완벽히 하기 어렵다. 그런데 잘못된 설정이 어떻게 동작하는지 이해하지 못하면, 나중에 고치려 해도 어디서부터 손대야 할지 모른다. Linux netfilter의 패킷 처리 경로, iptables/nftables 규칙 모델, AWS VPC 보안 그룹과 NACL의 차이를 운영자 관점에서 정리한다.
Linux netfilter: 커널 패킷 처리 프레임워크
iptables, nftables, conntrack은 모두 Linux 커널의 netfilter 프레임워크 위에서 동작한다. netfilter는 패킷 처리 경로의 특정 지점에 훅(hook)을 정의하고, 각 훅에 등록된 핸들러가 순서대로 실행된다.
훅은 5개다: PREROUTING(수신 직후, 라우팅 결정 전), INPUT(로컬 프로세스로 가는 패킷), FORWARD(다른 인터페이스로 전달하는 패킷), OUTPUT(로컬 프로세스가 생성한 패킷), POSTROUTING(라우팅 결정 후, 인터페이스 송출 직전).
테이블은 4개다: raw(conntrack 이전 처리, NOTRACK), mangle(TTL/DSCP 마킹), nat(DNAT/SNAT), filter(허용/거부 결정의 핵심). 지정하지 않으면 filter 테이블을 사용한다.
iptables 핵심 개념
체인·매치·타겟
iptables [-t 테이블] 커맨드 체인 [매치 조건] [-j 타겟]
# 예: INPUT 체인에서 10.20.1.0/24의 TCP 3306 신규 연결 허용
iptables -A INPUT -p tcp --dport 3306 -s 10.20.1.0/24 \
-m conntrack --ctstate NEW -j ACCEPT주요 타겟: ACCEPT(허용), DROP(무음 폐기, 응답 없음), REJECT(폐기 + ICMP/RST 오류 응답), LOG(로그 후 다음 규칙 진행 — 종단 타겟이 아님).
conntrack 기반 stateful 방화벽
-m conntrack --ctstate 매치로 연결 상태를 필터링한다.
| 상태 | 의미 |
|---|---|
NEW | 첫 패킷 (TCP SYN, UDP 첫 패킷). 아직 응답 없음 |
ESTABLISHED | 양방향 트래픽 확인됨. TCP는 SYN+ACK 이후 |
RELATED | 기존 연결과 관련된 새 연결 (FTP 데이터 채널, ICMP 오류) |
INVALID | 알려진 연결 상태와 맞지 않는 패킷 (RST 없이 도착한 ACK 등) |
# stateful 방화벽의 핵심 3행
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROPESTABLISHED,RELATED 한 줄로 허용된 아웃바운드 연결의 응답 트래픽이 전부 통과한다. INVALID 패킷을 명시적으로 DROP하는 것은 보안상 중요하다.
DB 서버 iptables 실전 설정
#!/bin/bash
# MySQL 서버 방화벽 (화이트리스트 방식)
iptables -F; iptables -X
iptables -P INPUT DROP # 기본 정책: DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# SSH: 관리 IP만 허용
iptables -A INPUT -p tcp --dport 22 -s 10.10.0.0/24 \
-m conntrack --ctstate NEW -j ACCEPT
# MySQL: 앱 서버 서브넷만 허용
iptables -A INPUT -p tcp --dport 3306 -s 10.20.1.0/24 \
-m conntrack --ctstate NEW \
-m comment --comment "Allow MySQL from app subnet" \
-j ACCEPT
# 복수 소스: IP 범위 매치
iptables -A INPUT -p tcp --dport 3306 \
-m iprange --src-range 10.20.2.10-10.20.2.20 \
-m conntrack --ctstate NEW -j ACCEPT
# ICMP ping 허용 (모니터링용)
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# 드롭 패킷 로깅 (속도 제한 포함)
iptables -A INPUT -m limit --limit 5/min --limit-burst 10 \
-j LOG --log-prefix "IPT-DROP: " --log-level 4IP당 연결 수 제한 (connlimit):
# IP 하나에서 MySQL 동시 연결을 10개로 제한
iptables -A INPUT -p tcp --dport 3306 \
-m connlimit --connlimit-above 10 --connlimit-mask 32 \
-j REJECT --reject-with tcp-resetSYN Flood 방지:
# hashlimit으로 소스 IP별 SYN 속도 제한
iptables -A INPUT -p tcp --syn \
-m hashlimit --hashlimit-name synflood \
--hashlimit-upto 10/second --hashlimit-burst 20 \
--hashlimit-mode srcip -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP # 초과분
# 커널 SYN Cookie 함께 활성화
sysctl -w net.ipv4.tcp_syncookies=1규칙 확인 명령
# 카운터·인터페이스 포함 상세 조회
iptables -L -v -n
# 줄 번호 표시 (삭제 시 필요)
iptables -L INPUT --line-numbers
# NAT 테이블 조회
iptables -t nat -L -v -n
# conntrack 테이블 조회
conntrack -L | grep dport=3306
# 현재 추적 중인 연결 수 / 최대 한도
cat /proc/sys/net/netfilter/nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
# 영구 저장 (Debian/Ubuntu)
iptables-save > /etc/iptables/rules.v4nftables: iptables의 현대적 대체제
RHEL 8+, Ubuntu 20.04+에서 iptables 명령은 실제로 nftables 위의 호환 레이어(iptables-nft)를 가리킨다. 커널 내부는 이미 nftables다.
iptables의 문제점 3가지:
- IPv4는
iptables, IPv6는ip6tables, ARP는arptables— 동일한 정책을 각각 따로 작성해야 한다. - 규칙이 늘수록 O(n) 선형 탐색. 대규모 IP 목록 처리는 외부
ipset필요. - 규칙 변경 중 보안 공백 가능 (원자적 업데이트 불가).
nftables의 개선점:
inet패밀리로 IPv4·IPv6를 단일 ruleset에서 처리.- 내장 sets/maps: O(1) 해시 룩업. 수만 개 IP를 단일
set으로 관리. - 전체 ruleset을 단일 커널 트랜잭션으로 원자적 교체.
netdev패밀리: NIC 드라이버 직후의 ingress 훅 — conntrack 이전에 DDoS 패킷을 가장 이른 시점에 드롭.
nftables 기본 문법
# 테이블·체인·규칙 구조
# /etc/nftables.conf
flush ruleset
table inet filter {
# 내장 set: O(1) 룩업으로 다수 IP 처리
set app_servers {
type ipv4_addr
flags interval
elements = { 10.20.1.0/24, 10.20.2.10-10.20.2.20 }
}
chain input {
type filter hook input priority filter; policy drop;
iif lo accept
ct state invalid drop
ct state { established, related } accept
# set 참조: @기호
ip saddr @app_servers tcp dport 3306 \
ct state new counter accept \
comment "MySQL from app servers"
ip saddr 10.10.0.0/24 tcp dport 22 ct state new accept
icmp type echo-request accept
limit rate 5/minute log prefix "nft-drop: " level info
drop
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}set 동적 관리:
# 원소 추가/삭제 (서비스 중단 없음)
nft add element inet filter app_servers { 10.20.3.50 }
nft delete element inet filter app_servers { 10.20.1.0/24 }
# 타임아웃이 있는 동적 블랙리스트
nft add set inet filter blocklist { type ipv4_addr; flags timeout; timeout 1h; }
nft add element inet filter blocklist { 203.0.113.99 }iptables → nftables 변환:
# 단일 규칙 변환 확인
iptables-translate -A INPUT -p tcp --dport 3306 -s 10.20.1.0/24 -j ACCEPT
# → nft add rule ip filter INPUT ip saddr 10.20.1.0/24 tcp dport 3306 counter accept
# 전체 ruleset 변환
iptables-save | iptables-restore-translate -f > /etc/nftables.conf
# 적용·확인
nft -f /etc/nftables.conf
nft list rulesetAWS VPC Security Groups
핵심 특성: Stateful
Security Group에서 가장 중요한 특성은 stateful이다. 인바운드 트래픽을 허용하면 해당 연결의 응답 트래픽은 아웃바운드 규칙 없이도 자동 허용된다. 반대 방향도 동일하다.
예시:
인바운드: TCP 3306 허용 (소스: app-sg)
아웃바운드: 규칙 없음
→ MySQL 쿼리와 응답 모두 정상 통신 (stateful 덕분)Security Group은 ENI(Elastic Network Interface) 단위로 적용되며, 허용 규칙만 존재한다(명시적 DENY 없음). 어느 규칙에도 매치되지 않으면 묵시적 DENY다.
Security Group ID 참조 패턴 — DB 티어 격리
[인터넷]
│
[ALB] ─── alb-sg (80, 443 허용)
│
[앱 서버] ─── app-sg (alb-sg에서 8080 허용)
│
[RDS MySQL] ─── db-sg (app-sg에서 3306만 허용)db-sg 인바운드 규칙을 설정할 때 소스를 10.20.1.0/24 CIDR 대신 app-sg의 Security Group ID로 지정하는 것이 핵심이다. Auto Scaling으로 앱 서버 IP가 바뀌어도 규칙 변경이 필요 없다.
# AWS CLI: SG ID 참조로 DB 접근 허용
aws ec2 authorize-security-group-ingress \
--group-id sg-db0001 \
--ip-permissions '[{
"IpProtocol": "tcp",
"FromPort": 3306,
"ToPort": 3306,
"UserIdGroupPairs": [{
"GroupId": "sg-app0001",
"Description": "MySQL from app tier"
}]
}]'
# 위험한 SG 찾기: 3306을 0.0.0.0/0에 개방한 SG 전수 조회
aws ec2 describe-security-groups \
--filters \
"Name=ip-permission.from-port,Values=3306" \
"Name=ip-permission.to-port,Values=3306" \
"Name=ip-permission.cidr,Values=0.0.0.0/0" \
--query 'SecurityGroups[*].[GroupId,GroupName,VpcId]' \
--output tableAWS VPC Network ACLs (NACLs)
Security Group vs NACL 핵심 차이
| 항목 | Security Group | NACL |
|---|---|---|
| 상태 | Stateful (응답 자동 허용) | Stateless (응답 별도 규칙 필요) |
| 적용 단위 | ENI (개별 리소스) | 서브넷 전체 |
| 규칙 평가 | 모든 규칙 합산 | 번호 순서대로, 첫 매치에서 종료 |
| 규칙 타입 | 허용만 가능 | 허용 + 명시적 거부 가능 |
| 기본 규칙 | 모든 인바운드 차단 | 기본 NACL: 전체 허용 / 커스텀: 전체 차단 |
NACL은 알려진 악성 CIDR을 서브넷 전체에서 즉시 차단할 때 유용하다. 규칙 번호가 낮을수록 먼저 평가된다. 낮은 번호에 DENY를 넣으면 뒤의 ALLOW를 무시할 수 있다.
에페머럴 포트 — DBA가 반드시 이해해야 할 개념
NACL은 stateless이므로, 응답 트래픽은 별도 아웃바운드 규칙으로 허용해야 한다. 응답은 클라이언트가 선택한 에페머럴(임시) 포트로 돌아간다.
- Linux (Amazon Linux, Ubuntu): 32768 – 60999
- Windows Server 2008+: 49152 – 65535
예시: 앱 서버(10.20.1.5) → DB 서버(10.0.1.10:3306)
요청: src=10.20.1.5:54321 dst=10.0.1.10:3306
응답: src=10.0.1.10:3306 dst=10.20.1.5:54321 ← 에페머럴 포트로 귀환DB 서브넷 NACL 설정 (NACL은 양방향 모두 설정 필요):
인바운드:
100 TCP 3306 10.20.1.0/24 ALLOW ← 앱 서버의 연결 요청아웃바운드 (에페머럴 포트 허용 누락이 가장 흔한 실수):
100 TCP 32768-60999 10.20.1.0/24 ALLOW ← Linux 클라이언트 응답
200 TCP 49152-65535 10.20.1.0/24 ALLOW ← Windows 클라이언트 응답실용적 접근: 다양한 OS를 지원해야 하면 1024-65535를 허용한다.
Defense in Depth: 계층별 방어 패턴
방화벽은 한 겹이 아닌 여러 계층으로 구성해야 한다. 한 계층이 잘못 설정되거나 뚫려도 다음 계층이 방어한다.
운영 진단 명령 요약
# iptables: 카운터·인터페이스 포함 조회
iptables -L -v -n
iptables -L INPUT --line-numbers # 줄 번호 (삭제 시 필요)
iptables -t nat -L -v -n # NAT 테이블
# conntrack: 연결 추적 테이블
conntrack -L # 전체 조회
conntrack -L | grep dport=3306 # MySQL 연결만
conntrack -E # 실시간 이벤트 스트림
conntrack -S # CPU별 통계
# nftables
nft list ruleset # 전체 ruleset
nft list chain inet filter input # 특정 체인
nft --handle list chain inet filter input # 핸들 번호 포함 (삭제용)
nft -c -f /etc/nftables.conf # 문법 검사 (적용 안 함)
# AWS CLI
aws ec2 describe-security-groups --group-ids sg-XXX # SG 상세
aws ec2 describe-network-acls --filters Name=vpc-id,Values=vpc-XXX운영 체크리스트
iptables/nftables:
ESTABLISHED,RELATED허용이 서비스별 허용 규칙보다 앞에 있는가INVALID패킷을 DROP하는 규칙이 있는가- 기본 정책이 DROP인가 (화이트리스트 방식)
- conntrack 테이블 용량이 충분한가 (
nf_conntrack_maxvsnf_conntrack_count) iptables-save/nft -f로 재부팅 후 규칙이 복원되는가
AWS VPC:
- RDS
PubliclyAccessible: false설정 - DB SG 인바운드 소스가
app-sgID 참조 (CIDR 아님) - 0.0.0.0/0에서 3306/5432를 허용하는 SG가 없는지 정기 감사
- NACL 아웃바운드에 에페머럴 포트(32768–60999 또는 1024–65535) 허용
- VPC Flow Logs 활성화 → CloudWatch 또는 S3로 저장
핵심 요약
- Linux 방화벽은 netfilter 위에서 동작한다. 패킷은 PREROUTING → INPUT/FORWARD → POSTROUTING 경로를 통과하며 각 훅에서 raw, mangle, nat, filter 테이블이 우선순위 순으로 실행된다.
- iptables는 conntrack 기반 stateful 검사를 제공한다.
ESTABLISHED,RELATED허용 +INVALIDDROP이 stateful 방화벽의 핵심이다. - nftables는 IPv4/IPv6 통합, 내장 sets(O(1) 룩업), 원자적 업데이트로 iptables를 대체한다. 현대 배포판의 실제 커널 레이어는 이미 nftables다.
- AWS Security Group은 stateful이고 ENI 단위로 동작한다. DB SG 소스를 SG ID로 참조하는 패턴이 Auto Scaling 환경에서 가장 안전하다.
- AWS NACL은 stateless다. 에페머럴 포트(32768–60999) 아웃바운드 허용을 빠뜨리면 응답 패킷이 드롭된다.
- Defense in Depth: OS 방화벽 + Security Group + NACL을 계층별로 구성해 단일 실수가 전체 차단을 우회하지 못하도록 한다.
References
- DigitalOcean, "A Deep Dive into Iptables and Netfilter Architecture", https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture
- nftables wiki, "Netfilter hooks", https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks
- nftables wiki, "Quick reference-nftables in 10 minutes", https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes
- Red Hat, "Getting started with nftables (RHEL 8)", https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/8/html/configuring_and_managing_networking/getting-started-with-nftables_configuring-and-managing-networking
- TuxCare, "iptables vs nftables", https://tuxcare.com/blog/iptables-vs-nftables/
- nixCraft, "Linux Iptables Limit SYN attacks", https://www.cyberciti.biz/tips/howto-limit-linux-syn-attacks.html
- AWS, "Control subnet traffic with network ACLs", https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html
- AWS, "Ephemeral ports — Network ACLs", https://docs.aws.amazon.com/vpc/latest/userguide/nacl-ephemeral-ports.html
- AWS, "Security group rules", https://docs.aws.amazon.com/vpc/latest/userguide/security-group-rules.html
- AWS RDS, "Controlling access with security groups", https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.RDSSecurityGroups.html
- AWS Database Blog, "Applying best practices for securing sensitive data in Amazon RDS", https://aws.amazon.com/blogs/database/applying-best-practices-for-securing-sensitive-data-in-amazon-rds/
- Fedora Magazine, "Network address translation part 2: the conntrack tool", https://fedoramagazine.org/network-address-translation-part-2-the-conntrack-tool/
- ThermalCircle, "Nftables Packet Flow and Netfilter Hooks in Detail", https://thermalcircle.de/doku.php?id=blog:linux:nftables_packet_flow_netfilter_hooks_detail