어느날 Docker 내부 컨테이너에 접속이 안되면(Docker Engine v28 이후)

✍️

🛠️

👤

⛓️‍💥 갑자기 접속 안되는 Docker 컨테이너

평소 각 컨테이너들에 -p 옵션을 주지 않고 내부에서만 사용하다 어느날 접속이 안되는 상황이 되었다. iptables FORWARD 테이블 쪽 이슈가 발생한걸로 예상하고 해당 FORWARD 테이블을 확인했다.

Chain FORWARD (policy ACCEPT)
target prot opt source destination
{내부 LAN 인터페이스 <-> Docker 인터페이스 Rule}
{Docker 인터페이스 <-> 내부 LAN 인터페이스 Rule}
DOCKER-USER 0 -- 0.0.0.0/0 0.0.0.0/0
DOCKER-FORWARD 0 -- 0.0.0.0/0 0.0.0.0/0

위와 같이 규칙을 추가해서 임시방편으로 했는데, 일정 기간 동안 잘 되다가 이후 Docker를 업데이트 하니 또 작동이 안됐다. FORWARD 규칙을 다시 체크해보니 그대로 되어 있는 상태였고 업데이트 이후 안되는 상황으로 추측됐고, Docker 업데이트 이후 무언가 변경점이 발생한걸로 생각되어 업데이트 히스토리를 체크해보았다.

Docker Engine v28.0.0 히스토리에서 Networking 부분을 체크해보았다.

Port publishing in bridge networks

dockerd now requires ipset support in the Linux kernel. moby/moby#48596

The iptables and ip6tables rules used to implement port publishing and network isolation have been extensively modified. This enables some of the following functional changes, and is a first step in refactoring to enable native nftables support in a future release. moby/moby#48815

If it becomes necessary to downgrade to an earlier version of the daemon, some manual cleanup of the new rules will be necessary. The simplest and surest approach is to reboot the host, or use iptables -F and ip6tables -F to flush all existing iptables rules from the filter table before starting the older version of the daemon. When that is not possible, run the following commands as root:

(중략)

출처 : https://docs.docker.com/engine/release-notes/28/#networking-7

이번 패치로 네트워크 부분이 많이 수정되었다. 이후 도커 블로그에 접속하여 추가적으로 내용을 파악해보니 왜 이렇게 됐는지 확인 할 수 있었다.

What’s the impact?
This exposure would’ve required being on the same local network or otherwise having route-level access to the container’s RFC1918 IP range. It did not affect machines across the public internet. However, a malicious user could discover unpublished container ports, within LAN settings or corporate environments, and connect to them.
For instance, they might add a custom route to the container’s subnet, using the following command:
ip route add 172.17.0.0/16 via 192.168.0.10
From there, if 192.168.0.10 is your Docker host with a permissive firewall, the attacker could send packets straight to 172.17.0.x (the container’s IP).

출처 : https://www.docker.com/blog/docker-engine-28-hardening-container-networking-by-default/

내용을 요약하자면 보안 취약점의 통로로 사용 될 수 있어, -p 옵션이 주어진 포트만 개방하고, 이외의 포트로 컨테이너로 접속하지 못하도록 차단한다는 것이다.

문제는 필자와 같이 내부망을 복잡하게 구성한 경우와 같이, -p 옵션을 주지 않고는 내부망에 있는 다른 호스트가 Docker 컨테이너 서버에 자유롭게 접속하지 못하게 되는 문제가 발생한다.

🪄 해결 방법

크게 두가지의 방법이 있다.

Docker Engine 28.2.0 이후에 사용 가능하며, 다음과 같다. (v28 초기버전의 경우 조금 다르다. 링크로 이동하여 해당 블로그에서 내용을 참고 바란다.)

1번 : 브릿지 네트워크 생성시 “com.docker.network.bridge.trusted_host_interfaces”을 추가하여, 해당 인터페이스에서 통신 가능하도록 하는 옵션이다.

sudo docker network create --driver bridge \
--opt "com.docker.network.bridge.trusted_host_interfaces={인터페이스}" \
{네트워크 브릿지 이름}

위와 같이 네트워크 생성시 옵션을 주어야하며, 컨테이너들이 사용하던 기존 네트워크를 해당 네트워크로 변경해주어야 한다.

2번 : 도커 설정 파일에 “allow-direct-routing” 추가하여, 통신 가능하도록 하는 옵션이다.
/etc/docker/daemon.json 파일에 다음과 같이 수정한다. (없을 경우 새로 만들어야 한다.)

{
"allow-direct-routing": true
}

이후 sudo systemctl restart docker 명령어를 입력하여 서비스를 재시작하면 된다.

📒 여담

아무 생각 없이 서버 업데이트를 진행했다가 docker도 같이 업데이트되서 잘 사용하던 컨테이너 접속이 문제가되어 서비스들이 우르르 먹통이 되어 롤백하기도 해보고, docker 엔진을 교체해보고, iptables 테이블 규칙을 마구 수정하고 스트레스가 이만저만 아니였다. 업데이트 전에는 항상 패치 히스토리를 보고 진행하는 습관을 가져야할 것 같다.

목차