LLM WikiAccess-protected knowledge portal
← 스터디 홈
5편 · 약 30분

방화벽과 보안 그룹: iptables, nftables, VPC 규칙

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(라우팅 결정 후, 인터페이스 송출 직전).

NIC 수신 eth0 PREROUTING raw → conntrack mangle → nat(DNAT) 라우팅 로컬 목적지? 포워딩? 로컬 프로세스행 INPUT mangle → nat → filter 로컬 프로세스 mysqld, sshd 애플리케이션 OUTPUT raw → conntrack → filter 포워딩 경로 FORWARD mangle → filter POSTROUTING mangle → nat(SNAT/MASQ) NIC 송출 eth0 테이블 우선순위 (낮을수록 먼저): raw(-300) → mangle(-150) → nat(-100) → filter(0) conntrack은 raw 이후 -200 우선순위로 실행. filter는 INPUT/FORWARD/OUTPUT에서만 동작.
Linux netfilter 패킷 처리 경로

테이블은 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 DROP

ESTABLISHED,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 4

IP당 연결 수 제한 (connlimit):

# IP 하나에서 MySQL 동시 연결을 10개로 제한
iptables -A INPUT -p tcp --dport 3306 \
    -m connlimit --connlimit-above 10 --connlimit-mask 32 \
    -j REJECT --reject-with tcp-reset

SYN 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.v4

nftables: iptables의 현대적 대체제

RHEL 8+, Ubuntu 20.04+에서 iptables 명령은 실제로 nftables 위의 호환 레이어(iptables-nft)를 가리킨다. 커널 내부는 이미 nftables다.

iptables의 문제점 3가지:

  1. IPv4는 iptables, IPv6는 ip6tables, ARP는 arptables — 동일한 정책을 각각 따로 작성해야 한다.
  2. 규칙이 늘수록 O(n) 선형 탐색. 대규모 IP 목록 처리는 외부 ipset 필요.
  3. 규칙 변경 중 보안 공백 가능 (원자적 업데이트 불가).

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 ruleset

AWS 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-sgSecurity 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 table

AWS VPC Network ACLs (NACLs)

Security Group vs NACL 핵심 차이

항목Security GroupNACL
상태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: 계층별 방어 패턴

방화벽은 한 겹이 아닌 여러 계층으로 구성해야 한다. 한 계층이 잘못 설정되거나 뚫려도 다음 계층이 방어한다.

계층 1: OS 방화벽 (iptables/nftables) — EC2 기반 DB 서버
호스트 자체에 적용. Security Group이 잘못 설정되거나 내부망에서의 접근을 세밀하게 제어할 때 최후 방어선. 예: IP당 연결 수 제한, SYN Flood 방어, 복제 트래픽 허용, 관리 포트 IP 화이트리스트
계층 2: VPC Security Group — ENI 단위
Stateful. 허용 규칙만. app-sg ID를 소스로 지정해 앱 서버만 3306 접근 허용.
alb-sg: 80, 443 허용 (0.0.0.0/0) app-sg: 8080 (alb-sg 소스) db-sg: 3306 (app-sg 소스만)
계층 3: VPC NACL — 서브넷 단위
Stateless. 명시적 DENY 가능. 알려진 악성 CIDR을 낮은 번호 규칙으로 즉시 차단. 에페머럴 포트 아웃바운드 허용 필수.
10: DENY 악성 CIDR 100: ALLOW 3306 앱 서브넷 *: DENY ALL
RDS 설계 필수 원칙
PubliclyAccessible: false DB 서브넷에 IGW 라우팅 없음 SG 소스: CIDR 아닌 SG ID 사용 VPC Flow Logs 활성화
RDS 보안 3계층 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_max vs nf_conntrack_count)
  • iptables-save/nft -f로 재부팅 후 규칙이 복원되는가

AWS VPC:

  • RDS PubliclyAccessible: false 설정
  • DB SG 인바운드 소스가 app-sg ID 참조 (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 허용 + INVALID DROP이 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