ufw로 막아 둔 포트가 Docker로 띄운 DB에서는 외부에 열려 있던 이유 — 게시한 포트는 ufw 규칙에 닿기 전에 빠져나간다
문제 발생
ufw로 22·80·443만 허용해 둔 서버에서 Compose로 DB를 이렇게 띄우는 경우입니다.
services:
db:
image: postgres:17
ports:
- "5432:5432"ufw status에는 5432가 없습니다. 그런데 서버 밖의 PC에서 서버IP:5432로 접속이 됩니다. 허용한 적 없는 포트가 열려 있고, ufw 규칙을 다시 읽어도 이유가 보이지 않습니다.
원인 분석
게시한 포트로 가는 패킷은 ufw 규칙에 닿기 전에 방향이 바뀝니다. Docker 문서의 "Docker and ufw" 절이 그대로 적어 둡니다 — Docker와 ufw는 서로 호환되지 않는 방식으로 방화벽 규칙을 쓰고, 컨테이너 포트를 게시하면 그 트래픽은 ufw 설정을 거치기 전에 우회됩니다. Docker가 nat 테이블에서 컨테이너 트래픽을 라우팅하므로, 패킷은 ufw가 쓰는 INPUT·OUTPUT 체인에 도달하기 전에 빠져나갑니다.
그리고 호스트 주소를 적지 않으면 "모든 주소"입니다. Compose 명세의 짧은 문법은 [HOST:]CONTAINER[/PROTOCOL]이고, 호스트 IP를 적지 않으면 모든 네트워크 인터페이스(0.0.0.0)에 바인딩합니다. Docker 문서도 중요 표시로 경고합니다 — 컨테이너 포트 게시는 기본적으로 안전하지 않고, 게시한 포트는 Docker 호스트뿐 아니라 바깥 세계에도 열립니다.
즉 "5432:5432" 한 줄은 "호스트에서 쓰겠다"가 아니라 "바깥에 열겠다"에 가깝고, ufw는 그 결정에 끼어들지 못합니다.
해결 방안
-
다른 컨테이너만 쓰는 포트는 게시하지 않습니다. 문서에 따르면 브리지 네트워크의 컨테이너 포트는 게시하지 않아도 Docker 호스트와 같은 네트워크의 다른 컨테이너에서 접근할 수 있고, 호스트 밖에서는 접근할 수 없습니다. 앱 컨테이너가 DB와 같은 네트워크에 있다면
ports를 지웁니다. -
호스트에서만 필요하면 루프백 주소에 묶습니다. localhost 주소(
127.0.0.1또는::1)를 함께 적으면 Docker 호스트만 그 포트에 접근할 수 있습니다.
ports:
- "127.0.0.1:5432:5432"단, 28.0.0보다 오래된 릴리스에서는 같은 L2 세그먼트(같은 스위치에 연결된 호스트)에서 localhost에 게시한 포트에 닿을 수 있다는 경고가 문서에 있습니다. 엔진 버전을 함께 확인합니다.
- 기본 바인딩 주소를 바꿔 실수를 막습니다. 사용자 정의 브리지 네트워크 전체의 기본값은 데몬 설정의
default-network-opts로 정합니다. 기본 브리지 네트워크는daemon.json의"ip"키가 따로 있습니다.
{
"default-network-opts": {
"bridge": {
"com.docker.network.bridge.host_binding_ipv4": "127.0.0.1"
}
}
}- 특정 IP에만 열어야 하면
DOCKER-USER체인을 씁니다. 기본 백엔드인 iptables에서 Docker는filter테이블에 자체 체인을 만들고,DOCKER-USER는 Docker의 규칙보다 먼저 처리되는 사용자 규칙 자리입니다.FORWARD체인 뒤에 덧붙인 규칙은 Docker 규칙 다음에 처리되고, Docker 체인에서 이미 허용·거부된 패킷은 그 규칙에 보이지 않습니다.
iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.2 -j DROPext_if는 호스트의 실제 외부 인터페이스 이름으로 바꿉니다. 이 체인에 도착한 패킷은 이미 DNAT를 거친 뒤라 컨테이너의 내부 IP·포트로만 매칭되고, 원래 주소와 포트로 매칭하려면 conntrack 확장(--ctorigdst, --ctorigdstport)을 써야 합니다.
- 바깥에서 확인합니다.
docker container inspect <id> --format "{{json .NetworkSettings.Ports}}"의HostIp가0.0.0.0이면 모든 주소에 게시된 것입니다. 그다음 서버 밖의 기기에서nc -vz 서버IP 5432로 실제로 닫혔는지 봅니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.