2026년 11월 13일 금요일

ufw 를 켰는데 Docker 컨테이너 포트는 그대로 열려 있을 때 — DOCKER-USER 체인

홈 서버에 ufw 를 "기본 차단"으로 켜고 안심했는데, 같은 LAN 의 다른 PC 에서 컨테이너 포트가 그대로 열려 있었다. Docker 가 공개한 포트는 ufw 규칙을 거치지 않는다.

증상

sudo ufw default deny incoming
sudo ufw enable
sudo ufw status      # 허용 규칙은 VPN 인터페이스뿐

이 상태에서 호스트에서 직접 띄운 서비스는 LAN 에서 접근이 막힌다. 그런데 -p 8443:8443 처럼 포트를 공개한 컨테이너는 LAN 에서 여전히 접속된다.

원인

ufw 는 iptables 의 INPUT 체인을 관리한다. 호스트 자신이 목적지인 패킷이 지나는 길이다. 반면 Docker 는 공개 포트로 들어온 패킷을 NAT 로 컨테이너 주소로 바꿔서 FORWARD 경로로 보낸다. 패킷이 INPUT 을 지나지 않으니 ufw 의 규칙은 볼 기회가 없다. Docker 는 자기 규칙을 FORWARD 체인 앞쪽에 직접 넣어 관리하므로, ufw 의 "routed 기본 차단"보다도 먼저 처리된다.

해결

Docker 는 사용자가 규칙을 넣으라고 DOCKER-USER 라는 체인을 비워 두었다. 이 체인은 Docker 자신의 규칙보다 먼저 평가된다. ufw 를 다시 불러올 때도 유지되도록 /etc/ufw/after.rules 끝에 추가했다(IPv6 를 쓰면 after6.rules 에도 같은 내용).

*filter
:DOCKER-USER - [0:0]
# 이미 맺어진 연결의 응답은 통과
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
# LAN 쪽 인터페이스로 들어오는 새 연결은 차단
-A DOCKER-USER -i eth0 -j DROP
-A DOCKER-USER -i wlan0 -j DROP
COMMIT
sudo ufw reload

특정 포트만 LAN 에 열고 싶다면 DROP 앞에 예외를 넣는다.

-A DOCKER-USER -i eth0 -p tcp --dport 8443 -j RETURN

주의할 점은 DOCKER-USER 에서 보이는 포트가 NAT 이후의 포트라는 것이다. -p 8443:443 으로 공개했다면 규칙에는 컨테이너 쪽 포트 443 을 적어야 한다.

더 단순한 방법

외부에 열 필요가 없는 컨테이너라면 처음부터 루프백에만 바인딩하는 편이 낫다. 방화벽 규칙을 건드릴 필요가 없다.

ports:
  - "127.0.0.1:8080:3000"

정리

ufw 의 상태 출력은 Docker 공개 포트에 대해 아무것도 말해 주지 않는다. 설정한 뒤에는 반드시 다른 기기에서 실제로 포트를 두드려 확인하자. 내 경우 규칙 적용 뒤 LAN 에서는 ping 만 되고 모든 포트가 닫힌 것을 확인했다.

댓글 없음:

댓글 쓰기

반도체 주식을 보기 전에 그려 두는 지도 — 설계, 파운드리, 메모리, 장비, 소재, 후공정

"반도체주"라고 한 묶음으로 부르지만, 그 안의 회사들은 하는 일도 돈 버는 방식도 전혀 다르다. 같은 뉴스에 어떤 회사는 오르고 어떤 회사는 내리는 이유가 여기에 있다. 종목을 들여다보기 전에 머릿속에 그려 두면 좋은 지도를 정리한...