포트를 열었는데 외부에서 "Connection timed out"이 납니다. ss -tlnp에는 포트가 LISTEN 상태인데도 마찬가지입니다. 방화벽 규칙을 확인해야 하는데 이 서버에서 iptables, firewalld, ufw 중 어느 도구가 실제로 작동하는지도 모르는 상태입니다. 더 위험한 상황은 반대쪽에서 옵니다 — SSH 포트를 바꾸다 방화벽 오픈 없이 재시작해서 서버에서 완전히 잠기는 Lock-out 입니다.
방화벽 (firewalld & iptables)
- 1netfilter → iptables → Tables·Chains·Rules 3계층 구조를 설명할 수 있다
- 2iptables 규칙을 조회·추가·삽입·삭제하고 iptables-save로 영구 저장할 수 있다
- 3conntrack 모듈로 ESTABLISHED/RELATED 상태 기반 방화벽을 구성할 수 있다
- 4firewalld zone과 firewall-cmd, ufw로 서버 방화벽을 관리할 수 있다
- 5클라우드 보안그룹과 OS 방화벽 두 레이어를 함께 진단할 수 있다
iptables --version && iptables -L -n -vsystemctl status firewalld && firewall-cmd --stateufw status verboseiptables-save | grep -v '^#'Linux 방화벽 계층 구조: iptables → netfilter → 커널
확대
iptables -L로 확인하면 아무 규칙이 없는데, firewall-cmd --list-all로 보면 규칙이 있습니다. 어떤 도구로 설정해야 효과가 있는지, 두 도구가 서로 충돌하는 건지 파악하기 어렵습니다. 이 혼란의 근원은 Linux 방화벽이 계층으로 나뉘어 있기 때문입니다. 커널 안에 netfilter라는 패킷 필터링 엔진이 있고, 그 위에 iptables/nftables/firewalld/ufw가 각각 다른 방식으로 같은 엔진을 제어합니다. 어떤 도구가 어느 레이어에 있는지를 이해하면, 내가 설정한 규칙이 왜 작동하는지(또는 왜 안 먹히는지)가 명확해집니다.
Linux 방화벽은 여러 계층으로 구성되어 있습니다. 각 도구가 어느 레벨에서 동작하는지 이해하면 문제 해결이 훨씬 쉬워집니다.
확대
계층별 역할
- netfilter: Linux 커널에 내장된 패킷 필터링 프레임워크. 실제 패킷을 허용/차단하는 주체입니다.
- iptables: netfilter를 제어하는 전통적인 사용자 공간 도구. 현재도 RHEL 7 이하, 많은 레거시 시스템에서 표준으로 쓰입니다.
- nftables: iptables의 후속 도구. RHEL 8+, Ubuntu 20.04+에서는 내부적으로 nftables를 사용하지만 iptables 명령 호환성을 유지합니다.
- firewalld: Red Hat이 만든 동적 방화벽 관리 데몬. zone 기반으로 정책을 관리하며, 내부적으로 iptables 또는 nftables를 사용합니다.
- ufw (Uncomplicated Firewall): Ubuntu가 채택한 iptables 프론트엔드. 명령어가 단순하여 초보자도 쉽게 사용할 수 있습니다.
패킷이 통과하는 경로 (Netfilter Hook Points)
확대
이 흐름을 이해하면 "왜 INPUT 체인에 룰을 추가했는데 효과가 없지?"와 같은 의문이 해소됩니다.
iptables 핵심 구조: Tables, Chains, Rules
확대
iptables -A INPUT -p tcp --dport 80 -j ACCEPT를 실행했는데 왜 이 규칙이 앞에 있는 DROP 규칙 뒤에 붙어서 적용이 안 되는지, 왜 -I와 -A의 결과가 다른지, FORWARD 체인은 언제 쓰는지 — 이 질문들이 모두 iptables의 3단 구조에서 답이 나옵니다. 패킷이 커널에 들어오면 어떤 테이블의 어떤 체인을 거쳐서 어떤 룰과 매칭되는지, 그 흐름을 한 번 이해하면 이후 모든 iptables 명령이 논리적으로 연결됩니다.
iptables는 테이블(Table) → 체인(Chain) → 룰(Rule) 의 3단 계층 구조로 동작합니다.
Tables (테이블)
각 테이블은 특정 목적을 위해 존재합니다.
| 테이블 | 용도 | 주요 체인 |
|---|---|---|
| filter | 기본 패킷 필터링 (허용/차단) | INPUT, OUTPUT, FORWARD |
| nat | 주소 변환 (포트 포워딩, 마스커레이드) | PREROUTING, POSTROUTING, OUTPUT |
| mangle | 패킷 헤더 수정 (TTL, TOS, MARK) | 모든 체인 |
| raw | connection tracking 예외 처리 | PREROUTING, OUTPUT |
일반적인 서버 방화벽 작업은 거의 모두 filter 테이블에서 이루어집니다.
Chains (체인)
filter 테이블의 3가지 기본 체인:
INPUT : 외부 → 로컬 서버로 들어오는 패킷
OUTPUT : 로컬 서버 → 외부로 나가는 패킷
FORWARD : 서버를 통과하는 패킷 (라우터 역할 시 사용)
실무 관점에서 가장 중요한 것은 INPUT 체인입니다. 서버에 접속하는 모든 외부 연결이 이 체인을 통과합니다.
Rules (룰)
각 룰은 다음 요소로 구성됩니다:
매칭 조건 (Match Criteria)
- 프로토콜: -p tcp / -p udp / -p icmp
- 출발지 IP: -s 192.168.1.0/24
- 목적지 IP: -d 10.0.0.1
- 포트: --dport 80 / --sport 1024:65535
- 인터페이스: -i eth0 / -o eth0
처리 액션 (Target)
- ACCEPT : 패킷을 허용
- DROP : 패킷을 조용히 버림 (응답 없음)
- REJECT : 패킷을 버리고 오류 응답 반환
- LOG : 커널 로그에 기록 (이후 룰 계속 적용)
- DNAT : 목적지 주소 변환
- SNAT : 출발지 주소 변환
- MASQUERADE : 동적 SNAT
룰 적용 순서가 중요합니다. iptables는 위에서 아래로 순서대로 룰을 검사하고, 처음 매칭되는 룰이 적용됩니다. 이후 룰은 검사하지 않습니다.
룰 1: -s 10.0.0.5 -j DROP ← 이 룰이 먼저 매칭되면
룰 2: -s 10.0.0.5 -j ACCEPT ← 이 룰은 절대 실행되지 않음
기본 정책 (Default Policy)
체인의 기본 정책은 어떤 룰도 매칭되지 않을 때 적용됩니다.
# 현재 기본 정책 확인
iptables -L -n | grep "policy"
# 결과 예시
Chain INPUT (policy ACCEPT) ← 아무 룰도 없으면 허용
Chain FORWARD (policy DROP) ← 아무 룰도 없으면 차단
Chain OUTPUT (policy ACCEPT) ← 아무 룰도 없으면 허용
보안 강화 서버의 일반적인 정책:
- INPUT: DROP (화이트리스트 방식 — 명시적으로 허용된 것만 허용)
- OUTPUT: ACCEPT (나가는 트래픽은 대부분 허용)
- FORWARD: DROP (라우터가 아니므로 통과 패킷 차단)
주의: INPUT 정책을 DROP으로 변경하기 전에 반드시 SSH 허용 룰을 먼저 추가하세요. 순서를 바꾸면 서버에서 잠길 수 있습니다.
iptables 기본 명령어
실습 전 디렉토리와 예제 파일을 먼저 준비합니다.
# 실습 디렉토리 준비
mkdir -p /tmp/linux/part4/exam_20 && cd /tmp/linux/part4/exam_20
# 실습 디렉토리 준비 (방화벽 규칙 백업 디렉토리)
mkdir -p /tmp/linux/part4/exam_4 && cd /tmp/linux/part4/exam_4
이제 실습을 진행합니다.
방화벽 현황을 확인하는 것이 모든 작업의 시작입니다.
# 기본 룰 조회 (filter 테이블)
sudo iptables -L
# 상세 조회: -n (숫자로 표시), -v (패킷/바이트 카운터), --line-numbers (줄 번호)
sudo iptables -L -n -v --line-numbers
# 출력 예시:
# Chain INPUT (policy ACCEPT)
# num pkts bytes target prot opt in out source destination
# 1 12543 2.1M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
# 2 8901 1.8M ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
# 3 0 0 DROP all -- * * 192.168.100.50 0.0.0.0/0
# 특정 체인만 조회
sudo iptables -L INPUT -n -v
# nat 테이블 조회 (포트 포워딩 확인)
sudo iptables -t nat -L -n -v
# mangle 테이블 조회
sudo iptables -t mangle -L -n -v
컬럼 설명:
pkts: 해당 룰에 매칭된 패킷 수 (트래픽 분석에 유용)bytes: 매칭된 바이트 합계target: 적용될 액션 (ACCEPT, DROP 등)prot: 프로토콜 (tcp, udp, all 등)source: 출발지 IP/CIDRdestination: 목적지 IP/CIDR
- 먼저 iptables -L -n -v 에서 각 체인의 기본 정책(policy)을 확인한다 — 그 다음 규칙 목록 왼쪽 pkts/bytes 열을 읽는다. pkts가 0이면 아직 그 규칙에 매칭된 패킷이 없는 것이고, 숫자가 계속 증가하면 실제 트래픽이 매칭 중이다
- 규칙 순서가 매칭에 직접 영향을 준다 — 같은 포트에 ACCEPT와 DROP이 둘 다 있을 때 위에 있는 규칙이 먼저 적용된다. -A는 맨 아래 추가, -I는 맨 위 삽입이므로 허용 규칙을 차단 규칙보다 먼저 적용하려면 -I를 써야 한다
- DROP이고 pkts가 증가 중이면 → 트래픽은 오고 있지만 조용히 버리는 중(스텔스 차단). REJECT이고 pkts 증가 중이면 → 거부 메시지가 발신자에게 돌아가는 중. 외부 공격자에게는 DROP, 내부 서비스 디버깅에는 REJECT가 일반적으로 더 유용하다
룰 추가 (-A: Append)
# SSH 허용 (INPUT 체인 끝에 추가)
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP 허용
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# HTTPS 허용
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 특정 IP에서 오는 모든 트래픽 허용
sudo iptables -A INPUT -s 10.10.0.0/16 -j ACCEPT
# 특정 IP 차단
sudo iptables -A INPUT -s 203.0.113.50 -j DROP
# ICMP (ping) 허용
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# 이미 확립된 연결과 관련 패킷 허용 (중요! 나가는 요청의 응답이 들어올 수 있도록)
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 루프백 인터페이스 허용 (로컬 서비스 간 통신)
sudo iptables -A INPUT -i lo -j ACCEPT
룰 삽입 (-I: Insert)
# INPUT 체인의 1번 위치에 삽입 (최우선 적용)
sudo iptables -I INPUT 1 -s 10.10.0.100 -j ACCEPT
# 3번 위치에 삽입
sudo iptables -I INPUT 3 -p tcp --dport 8080 -j ACCEPT
-A는 체인의 맨 끝에 추가하고, -I는 지정한 위치에 삽입합니다. 기본 정책이 DROP인 환경에서는 삽입 위치가 매우 중요합니다.
룰 삭제 (-D: Delete)
# 룰 번호로 삭제 (먼저 --line-numbers로 번호 확인)
sudo iptables -L INPUT --line-numbers
sudo iptables -D INPUT 3 # INPUT 체인의 3번 룰 삭제
# 룰 내용으로 삭제 (추가할 때 사용한 명령의 -A를 -D로 변경)
sudo iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
# 체인의 모든 룰 초기화
sudo iptables -F INPUT # INPUT 체인만 초기화
sudo iptables -F # 모든 체인 초기화 (주의!)
기본 정책 변경
# INPUT 기본 정책을 DROP으로 변경 (반드시 SSH 허용 룰 먼저 추가 후 실행!)
sudo iptables -P INPUT DROP
# FORWARD 기본 정책을 DROP으로 변경
sudo iptables -P FORWARD DROP
# 다시 ACCEPT로 변경
sudo iptables -P INPUT ACCEPT
기본 보안 정책 구성 (실무 예시)
#!/bin/bash
# 실무 서버 방화벽 기본 설정 스크립트
# 1. 기존 룰 초기화
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
# 2. 기본 정책 설정 (일단 ACCEPT로 시작, 마지막에 DROP으로 변경)
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 3. 루프백 허용 (항상 첫 번째로)
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# 4. 이미 확립된 연결 허용 (응답 패킷이 들어올 수 있도록)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 5. SSH 허용 (관리 접속 — 특정 관리자 IP에서만 허용)
iptables -A INPUT -p tcp --dport 22 -s 203.0.113.0/24 -j ACCEPT
# 6. 웹 서비스 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 7. 특정 IP 차단 (차단 목록)
iptables -A INPUT -s 198.51.100.0/24 -j DROP
# 8. 포트 스캔 방어: 비정상 TCP 플래그 패킷 차단
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # NULL 스캔
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP # XMAS 스캔
iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
# 9. ICMP 제한적 허용 (ping은 허용하되 rate limit)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 3 -j ACCEPT
# 10. 차단된 패킷 로그 기록 (마지막 룰 전에 위치)
iptables -A INPUT -j LOG --log-prefix "IPTABLES-DROPPED: " --log-level 4
# 11. 나머지 모든 INPUT 차단
iptables -P INPUT DROP
echo "방화벽 룰 적용 완료"
iptables -L -n -v
특정 포트 허용/차단
# 포트 범위 허용 (10000~20000)
sudo iptables -A INPUT -p tcp --dport 10000:20000 -j ACCEPT
# UDP 포트 허용 (DNS)
sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT
# 특정 포트를 특정 IP에서만 허용 (DB 포트)
sudo iptables -A INPUT -p tcp --dport 3306 -s 10.0.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 3306 -j DROP # 나머지 IP에서는 차단
# 특정 인터페이스에서 오는 트래픽 허용
sudo iptables -A INPUT -i eth1 -p tcp --dport 8080 -j ACCEPT
로그 기록
# 차단 전 로그 기록 (LOG는 패킷을 계속 통과시킴)
sudo iptables -A INPUT -p tcp --dport 22 -s 0/0 -j LOG --log-prefix "SSH-ATTEMPT: "
sudo iptables -A INPUT -p tcp --dport 22 -s 0/0 -j DROP
# 로그 확인
sudo dmesg | grep "SSH-ATTEMPT"
sudo journalctl -k | grep "SSH-ATTEMPT"
# 또는
sudo tail -f /var/log/kern.log | grep IPTABLES
iptables 룰은 기본적으로 재부팅 시 사라집니다. 영구 저장을 위한 방법을 알아봅니다.
iptables-save / iptables-restore
# 현재 룰을 파일로 저장
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
# 파일 내용 예시:
# *filter
# :INPUT DROP [0:0]
# :FORWARD DROP [0:0]
# :OUTPUT ACCEPT [0:0]
# -A INPUT -i lo -j ACCEPT
# -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
# -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
# COMMIT
# 파일에서 룰 복원
sudo iptables-restore < /etc/iptables/rules.v4
RHEL/CentOS에서 영구 저장
# iptables-services 패키지 설치
sudo dnf install iptables-services -y
# 서비스 활성화
sudo systemctl enable iptables
sudo systemctl start iptables
# 현재 룰 저장 (재부팅 후에도 유지)
sudo service iptables save
# 또는
sudo iptables-save > /etc/sysconfig/iptables
# 저장된 룰 확인
cat /etc/sysconfig/iptables
Ubuntu/Debian에서 영구 저장
# iptables-persistent 패키지 설치 (설치 시 현재 룰을 저장할지 묻는 대화상자 표시)
sudo apt install iptables-persistent -y
# 룰은 /etc/iptables/rules.v4에 저장됨
# 나중에 룰 변경 후 다시 저장:
sudo netfilter-persistent save
# 서비스가 부팅 시 자동으로 룰 복원
sudo systemctl status netfilter-persistent
systemd를 이용한 방법 (배포판 무관)
# /etc/systemd/system/iptables-restore.service 생성
sudo tee /etc/systemd/system/iptables-restore.service << 'EOF'
[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable iptables-restore
sudo systemctl start iptables-restore
firewalld (RHEL/CentOS 기본 방화벽)
firewalld는 zone(영역) 기반으로 네트워크 트래픽을 관리합니다. 각 zone은 신뢰 수준을 나타내며, 네트워크 인터페이스나 IP 주소를 특정 zone에 할당합니다.
Zone 종류와 기본 정책
| zone 이름 | 신뢰 수준 | 기본 동작 |
|---|---|---|
drop | 최저 | 모든 인바운드 패킷 드롭, 아웃바운드만 허용 |
block | 낮음 | 인바운드 거부 (ICMP 오류 반환), 아웃바운드 허용 |
public | 보통 | 선택한 서비스만 허용 (서버의 기본 zone) |
external | 보통 | NAT 마스커레이드 포함, 외부 연결용 |
internal | 높음 | 내부 네트워크용, 대부분 허용 |
dmz | 보통 | DMZ 서버용, 제한적 허용 |
work | 높음 | 업무 네트워크, 대부분 허용 |
home | 높음 | 홈 네트워크, 대부분 허용 |
trusted | 최고 | 모든 연결 허용 |
firewalld 서비스 관리
# firewalld 상태 확인
sudo systemctl status firewalld
# 시작/중지/재시작
sudo systemctl start firewalld
sudo systemctl stop firewalld
sudo systemctl restart firewalld
# 부팅 시 자동 시작
sudo systemctl enable firewalld
현재 상태 조회
# 전체 현황 조회
sudo firewall-cmd --list-all
# 출력 예시:
# public (active)
# target: default
# icmp-block-inversion: no
# interfaces: eth0
# sources:
# services: dhcpv6-client ssh
# ports: 8080/tcp
# protocols:
# masquerade: no
# forward-ports:
# source-ports:
# icmp-blocks:
# rich rules:
# 모든 zone 조회
sudo firewall-cmd --list-all-zones
# 활성 zone 확인
sudo firewall-cmd --get-active-zones
# 기본 zone 확인
sudo firewall-cmd --get-default-zone
# 사용 가능한 서비스 목록
sudo firewall-cmd --get-services
# 특정 zone 조회
sudo firewall-cmd --zone=public --list-all
포트 및 서비스 추가/제거
# 포트 열기 (임시 — 재부팅 시 사라짐)
sudo firewall-cmd --add-port=8080/tcp
# 포트 열기 (영구 저장)
sudo firewall-cmd --permanent --add-port=8080/tcp
# 영구 설정 후 반드시 reload 실행
sudo firewall-cmd --reload
# 포트 범위 열기
sudo firewall-cmd --permanent --add-port=10000-20000/tcp
# UDP 포트 열기
sudo firewall-cmd --permanent --add-port=53/udp
# 포트 닫기
sudo firewall-cmd --permanent --remove-port=8080/tcp
sudo firewall-cmd --reload
# 서비스 이름으로 추가 (서비스 정의 파일 사용)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=mysql
sudo firewall-cmd --reload
# 서비스 제거
sudo firewall-cmd --permanent --remove-service=mysql
sudo firewall-cmd --reload
--permanent vs 임시 적용
# 임시 적용: 즉시 효과, 재부팅 또는 reload 시 사라짐
sudo firewall-cmd --add-port=9090/tcp
# 영구 적용: 설정 파일에 저장, 즉시 효과 없음
sudo firewall-cmd --permanent --add-port=9090/tcp
# 영구 설정을 즉시 반영
sudo firewall-cmd --reload
# 실무 패턴: 테스트 후 영구 적용
# 1. 임시로 포트 열기
sudo firewall-cmd --add-port=9090/tcp
# 2. 접속 테스트
curl http://localhost:9090
# 3. 문제 없으면 영구 저장
sudo firewall-cmd --permanent --add-port=9090/tcp
sudo firewall-cmd --reload
# 현재 임시 설정과 영구 설정 비교
sudo firewall-cmd --list-ports # 현재 활성 설정
sudo firewall-cmd --permanent --list-ports # 영구 저장된 설정
Zone 변경 및 특정 IP 허용
# 특정 소스 IP를 trusted zone으로 이동 (모든 포트 허용)
sudo firewall-cmd --permanent --zone=trusted --add-source=10.0.1.0/24
sudo firewall-cmd --reload
# 특정 IP에서 특정 포트만 허용 (rich rule)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.100" port port="3306" protocol="tcp" accept'
# 특정 IP 차단
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.50" reject'
# rich rule 목록 확인
sudo firewall-cmd --list-rich-rules
# 인터페이스를 다른 zone으로 변경
sudo firewall-cmd --permanent --zone=internal --change-interface=eth1
sudo firewall-cmd --reload
ufw (Ubuntu 기본 방화벽)
ufw(Uncomplicated Firewall)는 Ubuntu/Debian 계열에서 기본 제공하는 방화벽으로, iptables보다 훨씬 간단한 명령어를 제공합니다.
ufw 기본 조작
# ufw 상태 확인
sudo ufw status
# 상세 상태 확인 (포트별 규칙 표시)
sudo ufw status verbose
# ufw 활성화 (처음 활성화 시 SSH가 차단될 수 있으니 주의)
sudo ufw enable
# ufw 비활성화
sudo ufw disable
# 룰 초기화 (모든 룰 삭제 및 비활성화)
sudo ufw reset
포트 허용/차단
# 포트 허용 (프로토콜 지정)
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 53/udp
# 프로토콜 생략 시 tcp+udp 모두 허용
sudo ufw allow 8080
# 서비스 이름으로 허용
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
# 포트 범위 허용
sudo ufw allow 10000:20000/tcp
# 포트 차단
sudo ufw deny 3306/tcp
# 특정 IP에서만 허용
sudo ufw allow from 10.0.1.0/24 to any port 22
# 특정 IP를 특정 포트로 허용
sudo ufw allow from 203.0.113.10 to any port 80 proto tcp
# 특정 IP 전체 차단
sudo ufw deny from 198.51.100.50
룰 삭제
# 룰 번호로 삭제 (먼저 번호 확인)
sudo ufw status numbered
# 출력 예시:
# Status: active
# To Action From
# -- ------ ----
# [ 1] 22/tcp ALLOW IN Anywhere
# [ 2] 80/tcp ALLOW IN Anywhere
# [ 3] 443/tcp ALLOW IN Anywhere
# 번호로 삭제
sudo ufw delete 3
# 룰 내용으로 삭제
sudo ufw delete allow 8080/tcp
sudo ufw delete allow http
기본 정책 설정
# 기본 정책 설정 (보안 강화)
sudo ufw default deny incoming # 인바운드 기본 차단
sudo ufw default allow outgoing # 아웃바운드 기본 허용
# 허용할 서비스 추가 후 활성화
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
ufw 로그
# 로그 활성화
sudo ufw logging on
# 로그 레벨 설정
sudo ufw logging low # 차단된 패킷만
sudo ufw logging medium # 차단 + 허용된 새 연결
sudo ufw logging high # 모든 패킷
# 로그 확인
sudo tail -f /var/log/ufw.log
# 로그 비활성화
sudo ufw logging off
클라우드 보안그룹과 OS 방화벽
클라우드 환경(AWS, GCP, Azure 등)에서는 방화벽이 두 겹으로 존재합니다. 이를 이해하지 못하면 한쪽만 열고 "안 된다"는 결론을 내리게 됩니다.
트래픽 흐름
확대
두 레이어를 모두 통과해야 서비스에 접속 가능합니다.
AWS 보안그룹 vs OS 방화벽 차이
| 항목 | AWS 보안그룹 | OS 방화벽 (iptables 등) |
|---|---|---|
| 위치 | 하이퍼바이저 레벨 | VM 내부 커널 |
| 상태 저장 | 상태 저장형 (Stateful) | 기본 상태 없음 (Stateless), conntrack 모듈로 상태 저장 가능 |
| 기본 정책 | 인바운드 전체 차단, 아웃바운드 허용 | 배포판마다 다름 |
| 적용 시점 | 즉시 (서비스 재시작 불필요) | 즉시 |
| 우선순위 | 먼저 통과해야 OS까지 도달 | 클라우드 보안그룹 통과 후 적용 |
실무 체크리스트
외부 접속이 안 될 때:
□ 1. 클라우드 보안그룹에서 해당 포트가 열려 있는가?
□ 2. OS 방화벽(iptables/firewalld/ufw)에서 해당 포트가 허용되어 있는가?
□ 3. 애플리케이션이 실제로 해당 포트에서 리슨하고 있는가?
□ 4. 애플리케이션이 0.0.0.0(모든 인터페이스)에서 리슨하는가, 127.0.0.1에서만 리슨하는가?
실무 예시: 웹 서버 포트 80 열기
# Step 1: AWS 콘솔 또는 CLI에서 보안그룹 설정
aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxxxxx \
--protocol tcp \
--port 80 \
--cidr 0.0.0.0/0
# Step 2: OS 방화벽 설정 (RHEL/CentOS)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
# Step 3: 애플리케이션이 모든 인터페이스에서 리슨하는지 확인
sudo ss -tlnp | grep :80
# tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234,fd=6))
# ↑ 0.0.0.0이어야 외부에서 접속 가능
# 127.0.0.1이면 로컬에서만 접속 가능
NAT & 포트 포워딩
포트 포워딩은 외부에서 들어오는 특정 포트 트래픽을 내부 서버의 다른 포트로 전달하는 기술입니다. 로드밸런서 없이 간단한 트래픽 리다이렉션에 유용합니다.
DNAT (Destination NAT) 기본 개념
확대
포트 포워딩 설정
# 외부 포트 80을 로컬 8080으로 포워딩
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
# 외부 포트 80을 다른 서버(192.168.1.10)의 8080으로 포워딩
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:8080
# FORWARD 체인에서 전달 허용
sudo iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 8080 -j ACCEPT
# 응답 패킷을 위한 MASQUERADE (POSTROUTING)
sudo iptables -t nat -A POSTROUTING -j MASQUERADE
# 커널 IP 포워딩 활성화 (필수)
echo 1 > /proc/sys/net/ipv4/ip_forward
# 영구 활성화 (/etc/sysctl.conf)
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
NAT 테이블 확인
# nat 테이블 룰 조회
sudo iptables -t nat -L -n -v
# 출력 예시:
# Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
# pkts bytes target prot opt in out source destination
# 5 300 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:192.168.1.10:8080
# firewalld로 포트 포워딩 설정
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080
sudo firewall-cmd --reload
# 다른 IP로 포워딩
sudo firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.1.10
sudo firewall-cmd --reload
마스커레이드 (MASQUERADE) — NAT 게이트웨이
# 내부 네트워크(192.168.1.0/24)가 외부 인터넷에 접속할 수 있도록 SNAT 설정
sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# firewalld로 마스커레이드 설정
sudo firewall-cmd --permanent --zone=external --add-masquerade
sudo firewall-cmd --reload
트러블슈팅
상황: 프로세스가 포트에 LISTEN하고 있는데 외부에서 접속이 안 됩니다. 서버 내부에서 curl localhost:80은 정상인데 외부에서는 timed out입니다.
원인: 클라우드 보안그룹 차단, OS 방화벽(iptables/firewalld/ufw) 차단, 또는 127.0.0.1에만 바인딩된 경우 중 하나입니다.
진단
Step 1: 프로세스가 실제로 리슨 중인지 확인
# 포트 80에서 리슨 중인 프로세스 확인
sudo ss -tlnp | grep :80
sudo netstat -tlnp | grep :80
# 결과 해석:
# tcp LISTEN 0.0.0.0:80 → 모든 인터페이스에서 리슨 (외부 접속 가능)
# tcp LISTEN 127.0.0.1:80 → 루프백에서만 리슨 (외부 접속 불가!)
# (아무것도 없음) → 프로세스가 해당 포트에서 리슨하지 않음
127.0.0.1에서만 리슨 중인 경우: 애플리케이션 설정에서 bind address를 0.0.0.0으로 변경하세요.
Step 2: OS 방화벽 확인
# iptables 확인
sudo iptables -L INPUT -n -v --line-numbers
# firewalld 확인
sudo firewall-cmd --list-all
sudo systemctl status firewalld
# ufw 확인
sudo ufw status verbose
Step 3: 클라우드 보안그룹 확인
# AWS: 현재 인스턴스의 보안그룹 확인
aws ec2 describe-instance-attribute --instance-id i-xxxxxxxx --attribute groupSet
# 보안그룹 인바운드 룰 확인
aws ec2 describe-security-groups --group-ids sg-xxxxxxxx --query 'SecurityGroups[*].IpPermissions'
Step 4: 서버 내부에서 직접 테스트
# 서버 자신에서 접속 테스트 (방화벽 우회)
curl http://localhost:80
curl http://127.0.0.1:80
# 서버 자신의 외부 IP로 테스트
curl http://$(curl -s ifconfig.me):80
Step 5: 외부에서 포트 연결 테스트
# 다른 서버에서 nc(netcat)로 포트 연결 테스트
nc -zv 서버IP 80
# 성공: Connection to 서버IP 80 port [tcp/http] succeeded!
# 실패: nc: Connection refused (포트는 열림, 프로세스 없음)
# 실패: nc: Operation timed out (방화벽이 DROP으로 차단)
# telnet으로 테스트
telnet 서버IP 80
Step 6: tcpdump로 패킷 도달 여부 확인
# 서버에서 실행 (외부에서 접속 시도 중에)
sudo tcpdump -i eth0 port 80 -n
# 패킷이 보이면 → OS까지 도달함 → OS 방화벽이나 프로세스 문제
# 패킷이 안 보이면 → 클라우드 보안그룹이 차단 중
해결 — 원인별 조치
| 증상 | 원인 | 해결 방법 |
|---|---|---|
| tcpdump에 패킷 없음 | 클라우드 보안그룹 차단 | 보안그룹 인바운드 룰 추가 |
| tcpdump에 패킷 있음, 응답 없음 | OS 방화벽 차단 | iptables/firewalld 룰 추가 |
| 방화벽 통과, 연결 거부 | 프로세스 미실행 또는 127.0.0.1 바인딩 | 프로세스 시작 또는 바인드 주소 변경 |
상황: SSH 포트를 22→2222로 변경하고 sshd를 재시작했는데 방화벽에 2222를 오픈하지 않아 서버 접속이 완전히 불가능합니다.
원인: 방화벽 오픈 → sshd 설정 변경 → 재시작 순서를 지키지 않아 sshd가 2222에 bind됐지만 방화벽 DROP 상태입니다.
진단 — 올바른 순서 (사전 예방)
잘못된 순서 (절대 금지):
# 위험한 순서 (절대 하지 마세요!)
# 1. /etc/ssh/sshd_config에서 Port를 2222로 변경
# 2. sshd 재시작 → Port 22에서 리슨 중단
# 3. 방화벽에서 2222 미오픈
# 4. 기존 SSH 세션 종료
# 5. 서버에 접속 불가 😱
올바른 순서 (반드시 이 순서로):
# 1. 먼저 새 포트를 방화벽에 추가
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
# 또는
sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
sudo iptables-save > /etc/iptables/rules.v4
# 2. 새 포트로 접속 테스트 (sshd 변경 전에)
# 다른 터미널에서: ssh -p 2222 user@서버IP
# (sshd가 아직 2222에서 안 리슨하므로 실패하지만, 방화벽은 열린 것 확인)
# 3. sshd 설정 변경
sudo vi /etc/ssh/sshd_config
# Port 2222 (또는 두 포트 모두: Port 22, Port 2222)
# SELinux가 활성화된 경우 SSH 포트 레이블 추가
sudo semanage port -a -t ssh_port_t -p tcp 2222
# 4. sshd 재시작 (기존 세션은 유지됨)
sudo systemctl restart sshd
# 5. 기존 세션 유지한 채로 새 포트로 접속 테스트
# ssh -p 2222 user@서버IP
# 6. 접속 성공 확인 후 기존 포트 22 차단 (선택사항)
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
해결 — Lock-out 발생 시 복구
이미 잠긴 경우 클라우드 플랫폼의 콘솔 접근을 이용합니다:
# AWS: EC2 Instance Connect 또는 Systems Manager Session Manager
aws ssm start-session --target i-xxxxxxxxxxxxxxxxx
# AWS: EC2 Serial Console (인스턴스 타입에 따라 지원)
# GCP: Cloud Shell에서 gcloud compute ssh
gcloud compute ssh 인스턴스명 --zone=asia-northeast3-a
# Azure: Serial Console 또는 Bastion
# 콘솔 접속 후 방화벽 룰 수정
sudo firewall-cmd --permanent --add-port=22/tcp
sudo firewall-cmd --reload
# 또는
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
예방 — at 명령으로 자동 복구 예약:
# at 명령으로 10분 후 방화벽 규칙 자동 복원 (안전망)
sudo at now + 10 minutes << 'EOF'
/sbin/iptables -F
/sbin/iptables -P INPUT ACCEPT
EOF
# 변경 작업 성공 시 at job 취소
sudo atq # 예약된 job 확인
sudo atrm 1 # job 번호 1 취소
상황: 특정 IP를 차단하는 DROP 룰을 -A(append)로 추가했는데 해당 IP에서 여전히 접속됩니다.
원인: iptables는 위에서 아래로 순서대로 매칭하며 처음 매칭되는 룰에서 처리를 종료합니다. ACCEPT ALL이 DROP보다 앞에 있으면 DROP은 절대 도달하지 않습니다.
진단
문제 사례 1: 특정 IP 차단이 효과 없는 경우
# 현재 룰 상태
sudo iptables -L INPUT -n --line-numbers
# 결과:
# Chain INPUT (policy ACCEPT)
# num target prot opt source destination
# 1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
# 2 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 ← 모든 IP에서 80 허용
# 3 DROP all -- 203.0.113.50 0.0.0.0/0 ← 특정 IP 차단 (효과 없음!)
# 203.0.113.50이 80 포트로 접속하면:
# 룰 1: 매칭 안됨 (기존 연결 아님)
# 룰 2: 매칭됨 → ACCEPT 적용, 여기서 처리 종료
# 룰 3: 절대 도달하지 않음
해결: 차단 룰을 허용 룰보다 앞에 배치
# 차단 룰을 1번 위치에 삽입
sudo iptables -I INPUT 1 -s 203.0.113.50 -j DROP
# 결과:
# 1 DROP all -- 203.0.113.50 ← 이제 먼저 검사됨
# 2 ACCEPT all -- 0.0.0.0/0 ctstate RELATED,ESTABLISHED
# 3 ACCEPT tcp -- 0.0.0.0/0 tcp dpt:80
문제 사례 2: ACCEPT ALL이 맨 앞에 있는 경우
# 잘못된 구성
# 1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ← 모든 트래픽 허용
# 2 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 ← 절대 실행 안됨
# 올바른 구성
# 1 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 ← 먼저 차단
# 2 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 ← 나머지 허용
해결 — 룰 순서 점검 및 수정
# 현재 룰을 순서대로 확인
sudo iptables -L INPUT -n -v --line-numbers
# 특정 IP/포트가 어느 룰에 매칭되는지 추적
# (iptables에는 trace 기능이 있음)
sudo iptables -t raw -A PREROUTING -p tcp --dport 80 -s 203.0.113.50 -j TRACE
sudo modprobe nf_log_ipv4
sudo sysctl net.netfilter.nf_log.2=nf_log_ipv4
# 로그 확인
sudo dmesg | grep "TRACE:"
# 추적 완료 후 TRACE 룰 제거
sudo iptables -t raw -D PREROUTING -p tcp --dport 80 -s 203.0.113.50 -j TRACE
권장 룰 순서 설계 원칙:
좋은 룰 순서 (권장):
1. 루프백 허용
2. ESTABLISHED/RELATED 허용
3. 명시적 차단 룰 (블랙리스트)
4. 서비스별 허용 룰
5. 관리 접속 허용 (SSH 등)
6. LOG 룰 (선택)
7. 기본 정책 (DROP)
실무 참고
방화벽 도구 선택 가이드
확대
새 서버에서 방화벽 설정을 하려는데 검색하면 ufw, firewalld, iptables, nftables가 동시에 나옵니다. 우분투 공식 문서는 ufw를 쓰고, RHEL 튜토리얼은 firewalld를 쓰고, Docker 관련 글은 iptables를 직접 건드립니다. 어떤 걸 써야 서로 충돌 없이 안정적으로 운영할 수 있는지 기준이 없으면, 나중에 규칙이 적용됐는지 안 됐는지 모르는 상황이 됩니다. 배포판과 용도 기준으로 어떤 도구를 선택해야 하는지 정리해두면 첫 설정 때 방향을 잡는 데 도움이 됩니다.
어떤 도구를 언제 사용해야 할지 혼란스러울 수 있습니다. 실무 기준으로 정리합니다.
배포판별 기본 도구
| 배포판 | 기본 방화벽 도구 | 내부 엔진 |
|---|---|---|
| RHEL/CentOS 7 | firewalld | iptables |
| RHEL/CentOS 8+ | firewalld | nftables |
| Ubuntu 18.04+ | ufw | iptables/nftables |
| Debian | iptables (수동) | iptables |
| Amazon Linux 2 | firewalld 또는 iptables | iptables |
| Amazon Linux 2023 | firewalld | nftables |
도구별 장단점
iptables
- 장점: 세밀한 제어, 모든 리눅스에서 사용 가능, 스크립트화 용이
- 단점: 명령어 복잡, 영구 저장을 별도로 처리해야 함, 동적 변경 어려움
- 적합한 상황: 복잡한 NAT 설정, 세밀한 패킷 필터링, 레거시 시스템
firewalld
- 장점: 동적 변경 (서비스 재시작 불필요), zone 기반 직관적 관리, D-Bus API
- 단점: 추상화로 인한 복잡성, RHEL 계열에 최적화
- 적합한 상황: RHEL/CentOS 서버, 동적으로 변경되는 포트 관리
ufw
- 장점: 단순한 명령어, 빠른 설정
- 단점: 세밀한 제어 어려움, Ubuntu에 최적화
- 적합한 상황: Ubuntu 서버, 간단한 포트 허용/차단
공존 문제 주의
# firewalld와 iptables를 동시에 사용하면 충돌 발생 가능
# RHEL에서 iptables를 직접 사용하려면 firewalld를 비활성화
sudo systemctl stop firewalld
sudo systemctl disable firewalld
sudo systemctl mask firewalld # 실수로 시작되지 않도록
# 그 후 iptables-services 사용
sudo systemctl enable iptables
sudo systemctl start iptables
nftables로의 전환
현대 Linux 배포판에서는 내부적으로 nftables를 사용하지만, iptables 명령은 호환성 레이어(iptables-nft)를 통해 계속 동작합니다.
# iptables가 nft 백엔드를 사용하는지 확인
iptables --version
# iptables v1.8.7 (nf_tables) ← nftables 백엔드 사용 중
# 직접 nftables 사용 (미래 방향)
sudo nft list ruleset
sudo nft add rule inet filter input tcp dport 80 accept
방화벽 관리자 혼용 금지 — 1개 도구만 사용하는 이유
확대
한 서버에서 ufw, firewalld, 직접 iptables, nftables를 동시에 사용하면 "룰이 있는데도 안 먹히는" 현상이 빈번하게 발생합니다.
혼용이 위험한 이유:
- ufw와 firewalld가 같은 netfilter 체인에 동시에 룰을 추가 → 충돌/누락
- firewalld direct(passthrough)는 deprecated 상태 — policies로 대체 예정
- nftables 환경에서는 ACCEPT가 즉시 허용을 의미하지 않을 수 있음 (chain/priority 의존)
현재 시스템의 방화벽 구조 확인:
# iptables 백엔드 확인 (nft vs legacy)
iptables --version
# iptables v1.8.10 (nf_tables) ← Ubuntu 22.04+, RHEL 9+ 기본
# iptables v1.8.4 (legacy) ← 구형 또는 명시적 전환 시
# 어떤 방화벽 관리자가 활성 상태인지 확인
systemctl is-active ufw firewalld 2>/dev/null
# 직접 nftables 룰 확인
sudo nft list ruleset | head -30
배포판별 권장 도구 (1개만 선택):
| 배포판 | 권장 도구 | 백엔드 |
|---|---|---|
| Ubuntu 20.04+ | ufw | iptables-nft |
| RHEL/Rocky/Alma 8+ | firewalld | nftables |
| Debian 12+ | ufw 또는 nftables 직접 | nftables |
| 컨테이너 호스트 (Docker) | iptables 직접 관리 | nft 또는 legacy |
운영 원칙: 신규 서버 구성 시 1개 도구를 선택하고 나머지는 비활성화하세요. ufw를 선택했다면
systemctl mask firewalld, firewalld를 선택했다면ufw disable.
IPv6 방화벽 기초 — ICMPv6 필수 허용과 ip6tables
확대
IPv4 방화벽만 신경 쓰고 IPv6를 방치하면 IPv6 경로로 방화벽을 우회하는 장애가 발생합니다. 또한 ICMPv6를 차단하면 IPv6 통신 자체가 완전히 불가능해집니다.
ip6tables 기본 사용:
# IPv6 규칙 조회
sudo ip6tables -L -n -v
# IPv6 SSH 허용 (IPv6 주소로 접속하는 경우)
sudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
# IPv6 규칙 영구 저장
sudo ip6tables-save > /etc/iptables/rules.v6
ICMPv6 필수 허용 규칙 (차단하면 IPv6 완전 불능):
# ICMPv6 필수 허용 (Neighbor Discovery, Router Advertisement 등)
# 이 규칙들을 차단하면 IPv6 주소 할당, 라우팅이 모두 실패함
sudo ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
sudo ip6tables -A OUTPUT -p ipv6-icmp -j ACCEPT
# 특정 ICMPv6 타입만 허용하는 더 엄격한 버전
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 135 -j ACCEPT # Neighbor Solicitation
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 136 -j ACCEPT # Neighbor Advertisement
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 133 -j ACCEPT # Router Solicitation
sudo ip6tables -A INPUT -p ipv6-icmp --icmpv6-type 134 -j ACCEPT # Router Advertisement
nftables로 IPv4/IPv6 통합 관리 (inet 패밀리):
# inet 패밀리는 IPv4와 IPv6를 동시에 처리 (중복 설정 불필요)
sudo nft add table inet filter
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
# SSH 허용 (IPv4 + IPv6 동시)
sudo nft add rule inet filter input tcp dport 22 accept
# ICMPv6 허용 (IPv4의 ICMP와 IPv6의 ICMPv6 각각 허용)
sudo nft add rule inet filter input icmp type echo-request accept
sudo nft add rule inet filter input icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert, nd-router-solicit } accept
# 규칙 확인
sudo nft list table inet filter
클라우드 환경: AWS/GCP/Azure의 Security Group은 IPv4와 IPv6를 별도 규칙으로 관리합니다. OS 방화벽뿐 아니라 Security Group에서도 IPv6 규칙을 추가했는지 확인하세요.
심화 — 상태 추적(conntrack)의 내부 동작과 한계
심화: conntrack 테이블 — 상태 방화벽이 실제로 '기억'하는 곳
앞에서 맨 앞에 넣은 ESTABLISHED,RELATED 한 줄이 나가는 요청의 응답을 통과시키는 이유는, 커널이 연결마다 상태를 따로 기억하고 있기 때문입니다. 그 '기억'이 저장되는 곳이 nf_conntrack 테이블이고, 이 테이블의 크기와 수명을 모르면 규칙이 멀쩡한데도 트래픽이 끊기는 장애를 만나게 됩니다.
- 엔트리 생성: 새 연결(NEW)이 생기면 커널이 출발지·목적지 IP/포트와 프로토콜(5-tuple)을 키로 엔트리를 만듭니다. 이후 들어오는 패킷은 이 테이블을 조회해 기존 엔트리에 속하면
ESTABLISHED, 연관되면RELATED로 분류됩니다. 규칙이 패킷마다 새로 판단하는 게 아니라 테이블 조회 결과로 상태가 정해집니다. - 크기와 수명: 테이블은 무한하지 않습니다.
nf_conntrack_max가 상한이고, 각 엔트리는 프로토콜별 타임아웃으로 만료됩니다. 확립된 TCP의 기본 타임아웃은 며칠로 길어서, 짧은 연결이 폭증하면 만료 전에 엔트리가 쌓입니다. - 가득 차면:
count가max에 도달하면 새 엔트리를 만들지 못해 NEW 패킷을 버립니다. 이때dmesg에nf_conntrack: table full, dropping packet이 찍힙니다. 규칙(iptables -L)은 완벽히 정상이라, 규칙만 봐서는 원인을 찾지 못합니다. - 한계와 튜닝: 상태 추적이 불필요한 고volume 트래픽(예: 대량 UDP·DNS)은 raw 테이블의
NOTRACK로 추적에서 제외할 수 있고, 타임아웃 단축·nf_conntrack_max상향(메모리와 트레이드오프)·해시 버킷 조정으로 대응합니다.
# 상태 추적 테이블 사용량과 상한 비교
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 삽입 실패·드롭 통계 (insert_failed, drop이 늘면 포화 신호)
sudo conntrack -S
상태 방화벽은 '규칙 + 상태 테이블'의 조합입니다. 규칙만 보면 절반만 보는 셈이고, 규모가 커질수록 이 테이블이 진짜 병목이 됩니다.
상황: 규칙을 바꾼 적이 없는데 트래픽이 몰리는 시간대에만 새 연결이 산발적으로 실패합니다. 이미 붙어 있던 세션은 끊기지 않고, iptables -L로 봐도 순서·정책 모두 정상입니다.
원인: nf_conntrack 테이블 포화입니다. 동시 연결과 짧은 연결이 한꺼번에 늘면서 nf_conntrack_count가 nf_conntrack_max에 도달했고, 새 5-tuple 엔트리를 만들지 못해 NEW 패킷이 버려집니다. 기존 엔트리에 속한 ESTABLISHED 패킷은 그대로 통과하므로, '기존 접속은 되는데 새 접속만 안 되는' 특징적 증상이 나타납니다.
진단: watch -n1 'cat /proc/sys/net/netfilter/nf_conntrack_count'로 상한 근접 여부를 보고, conntrack -S의 insert_failed·drop 증가를 확인합니다. conntrack -L을 프로토콜·포트로 집계해 어떤 트래픽이 엔트리를 대량 점유하는지 찾습니다. dmesg의 table full 시각과 접속 실패 시각이 일치하면 확정입니다.
해결: 근본은 '왜 엔트리가 이렇게 많은가'입니다 — 연결을 재사용하지 않는 앱이라면 keep-alive·커넥션 풀로 연결 수를 줄이고, 상태 추적이 필요 없는 대량 UDP는 raw 테이블 NOTRACK로 제외합니다. 급한 불은 nf_conntrack_max 상향과 해시 버킷 증설로 끄되, 이는 메모리를 더 쓰므로 근본 원인 해소와 병행해야 합니다. 재발 방지로 nf_conntrack_count/max 비율에 모니터링·알람을 걸어 포화 전에 감지합니다.
명령어·단축키 빠른 참조
이 모듈에서 다룬 방화벽 명령을 도구별로 모았습니다. iptables 규칙은 위→아래로 검사해 첫 매칭이 적용됩니다.
| 명령어/단축키 | 용도 | 자주 쓰는 예 |
|---|---|---|
iptables -L -n -v | 규칙·정책·카운터 조회 | iptables -L INPUT -n -v --line-numbers |
iptables -A / -I | 규칙 추가(끝)·삽입(위치) | -A INPUT -p tcp --dport 22 -j ACCEPT, -I INPUT 1 … |
iptables -D / -F | 규칙 삭제·체인 초기화 | iptables -D INPUT 3, iptables -F INPUT |
iptables -P | 체인 기본 정책 | iptables -P INPUT DROP(SSH 허용 룰 먼저!) |
-m conntrack --ctstate | 상태 기반 응답 허용 | --ctstate ESTABLISHED,RELATED -j ACCEPT(거의 1번 룰) |
iptables -t nat | NAT·포트포워딩 | -t nat -A POSTROUTING -j MASQUERADE, DNAT |
iptables-save / -restore | 규칙 영구 저장·복원 | iptables-save > /etc/iptables/rules.v4 |
firewall-cmd | firewalld zone·포트 관리 | firewall-cmd --permanent --add-port=8080/tcp && firewall-cmd --reload |
firewall-cmd --list-all | 현재 zone 규칙 조회 | firewall-cmd --get-active-zones |
ufw allow / enable | Ubuntu 간편 방화벽 | ufw allow ssh(먼저) → ufw enable |
ufw status | ufw 규칙 확인 | ufw status verbose, ufw status numbered |
nft list ruleset | nftables 규칙 조회·추가 | nft add rule inet filter input tcp dport 80 accept |
nc -zv / tcpdump | 포트 연결·패킷 도달 테스트 | nc -zv 서버IP 80, tcpdump -i eth0 port 80 -n |
nf_conntrack_count/max | 상태 테이블 포화 확인 | cat /proc/sys/net/netfilter/nf_conntrack_count |
at | 잘못된 규칙 자동 복원 예약 | at now + 10 minutes로 iptables -F 안전망 |
다음 모듈에서는 Linux 서버 프로비저닝 & 부팅 프로세스 — BIOS/UEFI, GRUB, systemd target까지 서버가 켜지는 순서와 부팅 장애 복구를 다룹니다.
클라우드에서는 iptables 대신 인스턴스 앞단의 가상 방화벽인 보안 그룹으로 같은 일을 합니다. 그 설계·운영은 가상 서버(EC2)(Cloud Engineering 트랙)에서 다룹니다.