나는 nginx 역방향 프록시를 이용해 특정 도메인을 특정 도커 컨테이너의 포트로 연결하는 방식으로 인프라를 구성해 운영하고 있다. 참고로, 이 환경은 ubuntu 기반이다. 이러한 설정은 외부로는 80번 포트와 443번 포트만 개방해 보안성을 높일 수 있다. 이를 통해 포트 스캐닝에 걸리지 않으며, 취약점을 줄일 수 있다.
기존에는 EC2와 같은 클라우드 서비스의 VM을 사용할 때 자체적으로 제공하는 방화벽 기능을 사용했지만, 라즈베리파이로 직접 서버를 구성하면서 방화벽 설정도 직접 해야 했다. 처음에는 iptables를 사용해 INPUT을 제한해보기도 하고, ufw로 제한해보기도 했지만 여전히 외부에서 컨테이너에 포트포워딩된 포트로의 접속이 가능했다.
방화벽 규칙이 적용되지 않았던 이유
이러한 방식은 도커가 자체적으로 생성하는 iptables 규칙을 우회할 수 없었기 때문이다. 도커는 컨테이너를 생성할 때 자동으로 iptables 규칙을 추가하여 네트워크를 관리한다. 이로 인해 사용자가 직접 설정한 규칙이 도커의 규칙보다 먼저 적용되지 않아서 외부 접근이 여전히 가능했던 것이다.
DOCKER-USER 체인을 통한 접근 제어 방법
도커는 자체적으로 DOCKER-USER라는 체인을 생성하여 사용자 정의 규칙을 적용할 수 있게 한다. 이 체인은 도커의 기본 규칙보다 먼저 실행되므로, 외부 접근을 차단하는 데 유용하다. 다음은 DOCKER-USER 체인을 활용하여 도커 컨테이너에 대한 외부 접근을 제한하고, 내부 네트워크와 Nginx 역방향 프록시를 통해서만 접근할 수 있도록 설정하는 방법이다.
1. DOCKER-USER 체인 초기화
기존에 설정된 불필요한 규칙을 제거하기 위해 DOCKER-USER 체인을 초기화합니다.
sudo iptables -F DOCKER-USER
2. 외부에서 접근을 허용할 포트 규칙 추가
외부에서 접근을 허용할 포트(80, 443, 22)에 대한 규칙을 추가합니다.
sudo iptables -A DOCKER-USER -p tcp --dport 80 -j RETURN
sudo iptables -A DOCKER-USER -p tcp --dport 443 -j RETURN
sudo iptables -A DOCKER-USER -p tcp --dport 22 -j RETURN
3. 내부 네트워크 접근 허용
사용중인 도커 브릿지 네트워크에서의 접근을 허용한다.
sudo iptables -A DOCKER-USER -s 172.23.0.0/16 -j RETURN
내부 통신을 허용한다.
sudo iptables -A DOCKER-USER -i docker0 -o docker0 -j ACCEPT
4. 나가는 트래픽 허용
나가는 트래픽을 허용한다.
sudo iptables -A DOCKER-USER -o eth0 -j ACCEPT
5. 기존 연결 허용
sudo iptables -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
6. 나머지 모든 외부 접근 차단
나머지 모든 외부 접근을 차단합니다.
sudo iptables -A DOCKER-USER -i eth0 -j DROP
이 규칙들은 다음과 같은 역할을 한다.
- 외부에서는 80, 443, 22번 포트만 접근이 가능하고, 나머지 포트는 모두 차단
- 내부 네트워크에서는 모든 포트에 접근이 가능
옵션은 이런 역할이다.
- A DOCKER-USER: -A는 규칙을 추가(append)하겠다는 의미. DOCKER-USER 체인은 도커가 생성하는 체인으로, 도커 컨테이너와 관련된 사용자 정의 규칙을 적용할 수 있는 체인.
- -i eth0: -i는 입력 인터페이스를 지정. eth0는 첫 번째 이더넷 네트워크 인터페이스를 의미. 따라서 이 규칙은 eth0 인터페이스를 통해 들어오는 트래픽에만 적용.
- -j DROP: -j는 패킷이 이 규칙과 일치할 때 수행할 동작을 지정. DROP은 패킷을 그냥 버리겠다는 의미. 이 경우, eth0 인터페이스로 들어오는 모든 패킷을 버림.
이렇게 하면 외부로부터의 트래픽을 모두 차단하고, 내부 네트워크 간의 트래픽만 허용할 수 있다.
22번 포트는 SSH 접속을 위해 잠시 개방해 두었는데(공개키 인증 사용 중), 특정 IP만 허용하도록 설정하면 보안성을 더욱 높일 수 있을 것이다. ( -i eth0 옵션을 주면 도커 브릿지 네트워크 허용은 안해도 될듯? 반대로 도커 브릿지 네트워크만 혀용하고 싶으면 -i eth0 옵션을 안넣으면 된다.)
최종적으로 iptables 규칙 확인
아래 명령어를 통해 현재 설정된 iptables 규칙을 상세하게 확인할 수 있다.
sudo iptables -L -v -n
iptables는 규칙을 위에서 아래로 순차적으로 평가하기때문에 순서에도 유의하여야 한다.
도커관련 iptables를 지워버렸다면?
혹여나 iptables를 모두 초기화 하여 도커 네트워크도 날아갔다면 걱정하지마라. 내가 그랬다..
도커를 재실행하면 다시 설정된다!!
sudo systemctl restart docker
iptables-persistent로 iptables 규칙 저장하기
iptables는 재부팅을하면 초기화가 된다. iptables 설정을 재부팅해도 유지하기 위해서는 iptables-persistent 패키지를 사용하면 된다. 이 패키지는 iptables 규칙을 저장하고 부팅 시 자동으로 로드한다.
1. iptables-persistent 패키지 설치
sudo apt-get install iptables-persistent
2. 현재 iptables 규칙 저장
sudo netfilter-persistent save
도커 네트워크의 ip 주소가 바뀐다면?
도커 컨테이너들간 내부 통신을 하고자 동일한 도커 네트워크로 설정해두었지만.. 서버를 재부팅할때마다 ip가 바뀌어버리는 문제가 있다. 물론 고정 ip를 할당하는 방법도 있지만.. 컨테이너를 종료하고 다시 설정해야하는 번거로움이 있다.
한가지 매우 간단한 방법이 있는데 컨테이너 이름을 사용하면 된다!
도커 컨테이너가 동일한 네트워크에 속해 있을 때, 컨테이너는 서로의 이름을 사용하여 통신할 수 있게 된다.
예를 들어, 'container1'에서 'container2'의 서비스를 사용하려면 'container2:포트번호'로 접근할 수 있다.