Bluehost Troubleshooting: UFW Rules

며칠전 Standard VPS NVMe 2를 구매하여 Coolify를 통해 Doker를 배포하는 과정에서 갑자기 문제가 생겼습니다. 잘되던 Coolify가 접속이 안되고, 설치했던 phpMyAdmin도 갑자기 접속이 안되었습니다.

Coolify VPS provider firewall (또는 cloud security group)을 우회하는 방식으로 임시 문제 해결하려 했으나 계속 이렇게 사용할 수는 없어서 근본적인 원인을 찾아 헤매기 시작했습니다.

ssh -L 8000:localhost:8000 sol1000@50.6.6.213

그 원인은 컨테이너가 많아 지면서 메모리 2GB로는 감당할 수 없게 되었고, 결국 재부팅을 했어야했는데 재부팅을 하는 과정에서 컨테이너의 내부IP가 변경되어 기존에 설정한 규칙이 작동하지 않게 되었던 것입니다.

Coolify가 처음 켜졌을때는 내부 IP가 172.18.0.10이었고, UFW 방화벽 차단규칙 (172.18.0.2 ~ 0.6만 차단)을 절묘하게 피해 가고 있어서 문제없이 작동했을 수 있습니다. 하지만 메모리부족으로 서버를 재부팅하면서 IP가 172.18.0.6으로 바뀌어버리면서 UFW의 차단 대역에 딱 걸리면서 갑자기 접속이 막힌 거죠. 이런 문제는 서버 재부팅 뿐만 아니라, Coolify의 자동업데이트나 서버가 내부적으로 컨테이너를 재시작 할때도 생길 수 있습니다.

원래는 Docker가 방화벽 규칙을 강제로 수정해서 UFW를 우회하고 있었는데 Ubuntu나 UFW패치가 진행되면서 보안상 위험하게 도커가 방화벽을 우회하는 것을 막고 /etc/ufw/after.rules에 있는 차단 규칙을 더 엄격하게 먼저 적용하겠다 라고 시스템 순서가 엄격해 졌을수도 있습니다.

도커(Docker)의 가장 큰 보안 취약점 중 하나가 “UFW 방화벽을 무시하고 지 마음대로 포트를 외부에 열어버리는 것”입니다. 예를 들어, 내가 방화벽으로 데이터베이스 포트를 막아놔도 도커로 실행하면 전 세계 누구나 내 데이터베이스에 접속할 수 있게 되는 치명적인 문제가 있습니다. Coolify는 서버를 안전하게 관리해 주는 플랫폼이다 보니, 설치 과정에서 이 도커의 보안 구멍을 강제로 메우려고 합니다. 그래서 설치 스크립트가 실행될 때 자동으로 /etc/ufw/after.rules 파일에 “도커가 방화벽을 멋대로 우회하지 못하게 막는 규칙(# BEGIN UFW AND DOCKER)”을 강제로 주입한 것입니다. 결국 이 사건은 “쿨리파이가 보안을 위해 자동으로 넣어둔 방화벽 규칙이, 도커의 유동 IP 특성과 만나면서 갑자기 자기 발등을 찍은 사건”인거죠. 정확히는 “방화벽 보안은 강화하고 싶은데, 도커(Docker)의 유동적인 IP 구조를 완벽하게 제어하지 못해 발생한 설계 미스”에 가깝습니다.

원래 의도한 정상적인 흐름은

외부 트래픽 ➔ 서버 (포트 80) ➔ 도커 엔진 ➔ 쿨리파이 프록시 컨테이너 ➔ 내 웹사이트

하지만 현재 꼬여버림 흐름은

외부 트래픽 ➔ 서버 (포트 80) ➔ 도커 엔진 ➔ [UFW 방화벽 차단 벽] ❌ ➔ 쿨리파이 프록시 컨테이너

쿨리파이가 설치될 때 /etc/ufw/after.rules에 주입한 방화벽 규칙은 “도커 내부 사설 IP 대역(172.16.0.0/12)으로 가는 트래픽을 기본적으로 다 막아라”였습니다. 그러면 쿨리파이 지가 컨테이너를 만들 때, “하지만 내가 서비스할 80, 443, 8080 포트는 예외로 열어둔다”라는 예외 규칙(ufw route allow)도 자동으로 방화벽에 같이 등록해 줬어야 합니다. 그런데 쿨리파이가 이 예외 처리를 똑바로 안 해두었거나, 컨테이너가 재시작되면서 내부 IP가 바뀔 때 방화벽 규칙을 동적으로 업데이트해 주지 못하는 바람에 자기 자신이 만든 방화벽 벽에 자기가 가두고 있던 컨테이너들이 전부 막혀버린 것입니다. 오픈소스 커뮤니티에서도 이 UFW와 도커의 충돌 문제는 쿨리파이 사용자들 사이에서 단골로 터지는 악명 높은 이슈 중 하나입니다.

Docker Container IP:

172.18.0.6 = coolify-proxy
172.18.0.5 = coolify
172.18.0.2 = coolify-realtime

/etc/ufw/after.rules에서 문제가 된 부분:

...
# BEGIN UFW AND DOCKER
...
-A DOCKER-USER -j ufw-user-forward

...
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 192.168.0.0/16
...
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT

결자해지: 수동으로 예외 구멍 뚫어주기

쿨리파이가 안 해놓은 예외 처리를 우리가 수동으로 뚫어주면 이 버그 상황을 해결할 수 있습니다. 아래 명령어들이 바로 “쿨리파이가 쓰는 내부 IP들로 향하는 포트들은 방화벽을 통과시켜라”라고 수동으로 예외를 지정해 주는 작업입니다.

# 1. 외부 웹 트래픽 통로 열기 (Coolify Proxy)
sudo ufw route allow proto tcp to 172.18.0.2 port 80
sudo ufw route allow proto tcp to 172.18.0.2 port 443

# 2. 쿨리파이 관리자 대시보드 창 열기 (Coolify)
sudo ufw route allow proto tcp to 172.18.0.8 port 8080

# 3. 실시간 알림 및 웹소켓 통로 열기 (Coolify Realtime)
sudo ufw route allow proto tcp to 172.18.0.7 port 6001
sudo ufw route allow proto tcp to 172.18.0.7 port 6002

# phpMyAdmin
sudo ufw route allow proto tcp to 172.20.0.2 port 80
sudo ufw route allow proto tcp to 172.19.0.3 port 80

# 4. 방화벽 규칙 새로고침
sudo ufw reload

이 규칙들을 적용하고 나면, 쿨리파이가 쳐놓은 방화벽 벽에 우리가 원하는 포트만 쏙쏙 통과할 수 있는 전용 통로가 생기게 됩니다.

참고로, Docker 컨테이너의 내부 IP를 확인하는 방법은 일단 docker ps로 컨테이너의 이름을 알아낸뒤 아래 명령어를 실행합니다.

sudo docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' coolify-proxy
172.18.0.2
172.19.0.2
sudo docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' coolify
172.18.0.8
sudo docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' coolify-realtime
172.18.0.7
sudo docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' phpmyadmin-zd7uebl3yu1e6y90mbts0x9w
172.20.0.2
172.19.0.3

컨테이너 같은 네트워크안에 넣기

두개의 컨테이너가 같은 네트워크에 연결이 안되어 있을때는 phpMyAdmin로그인이 안될수 있다. 그럴때는 네크워크를 하나 생성해서 두개의 컨테이너를 같은 네크워크로 묶으면 로그인이 된다.

# sudo docker network create mysql-pma-net
5ae87a43e2b0741a215f4623d711fc03d2984068a29fe1932a9cdc99c1b4afba

# sudo docker network connect mysql-pma-net kpfyrrcqrfu2x8r9sf4vno28
# sudo docker network connect mysql-pma-net phpmyadmin-zd7uebl3yu1e6y90mbts0x9w

*** 컨테이너를 Restart할때마다 네트워크 안에 다시 넣어주어야한다.