Journey to Security/리눅스

[방화벽] iptables - DNS 룰 설정

Cordilog 2026. 6. 3. 13:37

이전 포스팅에서 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 질의와 응답 경로가 모두 열린 것이다.