Docker 사용 시 서버와 같이 외부에서 컨테이너로 인바운드 연결을 차단하고 특정 포트만 허용할 때, iptable 설정 방법

문제 상황 및 원인

docker로 서버 운영 시, 여러 종류의 서비스들과 리버스 프록시를 연결시키는 시나리오 같은 상황에서 컨테이너 이름과 동일한 호스트 주소 별칭을 쓰지않고 로컬호스트에 직접 포트를 바인딩하여 쓰는 경우가 있다. 이때 이 포트에 대한 송수신 포트는 iptables의 INPUT 체인을 거치지않고 FORWARD 체인을 거쳐 DOCKER-USER, DOCKER, DOCKER-ISOLATION-STAGE를 거쳐 그대로 포워딩되기 때문에 아무리 INPUT 체인에서 정책을 DROP으로 주고 규칙을 설정해봤자 외부에서 포트에 접근할 수 있다.

해결 방법

이를 막기 위해서는 FORWARD 체인 다음올 패킷이 거쳐가면서, 자동 생성되는 나머지 DOCKER 체인 말고, 사용자 설정을 위한 DOCKER-USER 체인에 규칙을 추가하면 된다.

sudo iptables -I DOCKER-USER -i <당신의 이더넷 인터페이스 이름> -j DROP
sudo iptables -I DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

첫번째 명령어를 통해 이더넷 인터페이스를 통해 들어와서 도커 컨테이너를 향하는 모든 패킷은 드랍된다. 두번째 명령어는 컨테이너에서 인터넷을 향하는 아웃바운드 연결을 허용해주는 규칙이다. 빼먹으면 컨테이너에서 인터넷 접속이 안된다.

규칙을 확인하면 아래처럼 보일것이다.

$ sudo iptables -nvL
Chain DOCKER-USER (1 references)
 pkts bytes target     prot opt in     out     source               destination
  258  425K ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    0     0 DROP       all  --  enp??? *       0.0.0.0/0            0.0.0.0/0
   44  8729 RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0

특정 포트만 허용

위와 같이 실행하면 일단 컨테이너로의 모든 인바운드 연결이 막힌다. 특정 포트만 허용해주고 싶다면,

sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctorigdstport <port> --ctdir ORIGINAL -j ACCEPT

와 같이 명령을 내리면 된다. 위 명령어는 <port>번 포트로 들어오는 tcp 패킷을 ACCEPT한다. connection tracking 모듈을 사용(-m conntrack)하여 <port>번 포트를 향하는 (--ctorigdstport <port>) 패킷을 식별한다. 일반적인 경우에 사용되는 dpt를 사용하지 않는 이유는 NAT 변환으로 패킷의 실 경유 포트와 우리가 원하는 컨테이너에서 개방된 포트와 일치하지 않아 규칙이 적중하지 못할 수 있기 때문이다. --ctdir ORIGINAL을 통해 응답이 아닌 컨테이너를 향하는 요청만 규칙이 적중하게 한다. (나머지는 RELATED,ESTABLISHED 허용 규칙에 의해 처리된다.)

comments powered by Disqus