이전 포스팅에서 iptables의 기본 구조와 주요 명령어를 살펴봤다.
이번에는 실제로 방화벽 룰을 적용한 상태에서 의도치 않게 발생하는 문제를 해결해 보자.
1. SSH, HTTP, HTTPS 트래픽만 허용
먼저 filter table의 INPUT/OUTPUT 체인에 아래와 같이 룰을 추가한다.
# INPUT — 외부에서 서버로 들어오는 트래픽
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -j DROP
# OUTPUT — 서버에서 외부로 나가는 트래픽
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 443 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT
iptables -A OUTPUT -j DROP
적용 후 iptables -nL --line으로 확인하면 아래와 같다.

1️⃣ INPUT 체인 요약
TCP 443, 80, 22 포트만 허용하고 나머지는 전부 DROP한다.
웹 서비스와 SSH 접속만 받겠다는 의도다.
2️⃣ OUTPUT 체인 요약
TCP sport 80, 443, 22 포트만 허용하고 나머지는 DROP한다.
웹 서비스와 SSH의 응답 트래픽만 내보내겠다는 의도다.
2. 이 설정의 문제점
위 룰이 적용된 상태에서 dnf -y install bind-utils를 실행해보면 아래와 같은 에러가 발생한다.

패키지 다운로드 자체가 실패한 게 아니라, 도메인명을 IP 주소로 변환하는 DNS 조회 단계부터 막혀 있는 것이다.
DNS 해석이 안 되면 어느 서버에 요청해야 할지 알 수 없기 때문에 이후 단계로 진행되지 않는다.
3. 원인 분석
1️⃣ dnf install의 패킷 흐름
dnf install이 실행될 때 패킷은 네 단계를 거친다.
① DNS 질의 (서버 → DNS 서버, UDP dport 53) mirrors.rockylinux.org의 IP를 모르기 때문에 DNS 서버로 질의 패킷을 보낸다. OUTPUT 체인을 통과해야 한다.
② DNS 응답 (DNS 서버 → 서버, UDP sport 53) DNS 서버가 IP 주소를 알려주는 응답 패킷이 서버로 들어온다. INPUT 체인을 통과해야 한다.
③ 패키지 요청 (서버 → 미러 서버, TCP dport 80/443) IP를 알았으니 미러 서버로 다운로드 요청을 보낸다. OUTPUT 체인을 통과해야 한다.
④ 패키지 응답 (미러 서버 → 서버, TCP sport 80/443) 미러 서버가 패키지 데이터를 응답으로 돌려보낸다. INPUT 체인을 통과해야 한다.

2️⃣ 현재 설정의 문제
- OUTPUT 체인 — TCP spt 80/443/22만 허용. UDP dport 53, TCP dport 80/443이 없다 → DNS 질의 패킷이 DROP
- INPUT 체인 — TCP dpt 80/443/22만 허용. UDP sport 53, TCP sport 80/443이 없다 → DNS 응답 패킷이 DROP
1단계에서 이미 막히기 때문에 다음 단계부터는 실행 자체가 안 된다.
3️⃣ sport vs dport
방화벽 룰에서 포트 지정 방향은 어느 쪽이 먼저 연결을 시작하느냐에 따라 다르다.
| 방향 | 옵션 | 의미 |
| 서버 → 외부 (OUTPUT) | --dport | 내가 연결할 서비스의 목적지 포트 |
| 외부 → 서버 (INPUT) | --sport | 응답이 돌아오는 상대방 서버의 출발지 포트 |
DNS 응답은 DNS 서버의 53번 포트에서 출발해 내 서버의 랜덤 포트로 들어오기 때문에, OUTPUT에서는 --dport 53, INPUT에서는 --sport 53으로 잡아야 한다.
내 서버 → DNS 서버 질의: 출발 포트=랜덤(예: 43218) 도착 포트=53
DNS 서버 → 내 서버 응답: 출발 포트=53 도착 포트=43218
한편, 패키지 다운로드는 서버가 미러 서버에 먼저 요청을 보내는 구조다. 실제 패킷을 보면 이렇다.
내 서버 → 미러 서버 요청: 출발 포트=랜덤(예:52341) 도착 포트=80/443
미러 서버 → 내 서버 응답: 출발 포트=80/443 도착 포트=52341
이걸 각 체인 입장에서 보면 아래와 같다.
| 방향 | 체인 | 패킷의 dport | 패킷의 sport | 규칙 |
| 내 서버 → 미러 서버 | OUTPUT | 80 또는 443 | 랜덤 | --dport 80/443 |
| 미러 서버 → 내 서버 | INPUT | 랜덤 | 80 또는 443 | --sport 80/443 |
한 마디로, dport는 내가 접속하려는 서비스의 포트고, sport는 상대방 서버가 응답할 때 쓰는 포트다.
4. 해결 방법
1️⃣ 룰 삽입 위치
새 룰은 반드시 DROP 앞에 삽입해야 한다.
-A(append)를 쓰면 DROP 뒤에 추가되어 효력이 없기 때문에 -I CHAIN [행 번호]로 위치를 명시한다.
-I INPUT 4는 4번 자리에 새 룰을 추가하고 기존 룰을 한 칸씩 뒤로 밀어낸다.
기존 DROP은 7번으로 밀려나고, 새 룰이 그 앞을 채운다.
2️⃣ 룰 추가 명령
# INPUT 체인: DNS 응답 및 미러 서버 응답 허용
iptables -I INPUT 4 -p udp --sport 53 -j ACCEPT
iptables -I INPUT 4 -p tcp --sport 80 -j ACCEPT
iptables -I INPUT 4 -p tcp --sport 443 -j ACCEPT
# OUTPUT 체인: DNS 질의 및 미러 서버 요청 허용
iptables -I OUTPUT 4 -p udp --dport 53 -j ACCEPT
iptables -I OUTPUT 4 -p tcp --dport 80 -j ACCEPT
iptables -I OUTPUT 4 -p tcp --dport 443 -j ACCEPT
#추가된 룰 확인
iptables -nL --line

3️⃣ DNS 요청-응답 테스트 (패키지 설치)
dnf clean packages
dnf -y install bind-utils
정상 설치가 확인되면 DNS 질의와 응답 경로가 모두 열린 것이다.

'Journey to Security > 리눅스' 카테고리의 다른 글
| [방화벽] iptables Conntrack (2) INVALID 패킷 탐지 (0) | 2026.06.03 |
|---|---|
| [방화벽] iptables Conntrack (1) 상태 기반 룰 설정 (0) | 2026.06.03 |
| [방화벽] iptables 저장/복구 방법 (0) | 2026.06.02 |
| [방화벽] 리눅스 호스트 방화벽 iptables에 대해 알아보자 (0) | 2026.05.30 |
| Kali 리눅스 IP주소 설정 방법 (0) | 2026.05.13 |