Daeho's Dev Blog

Docker GitLab 특정 IP만 접근 허용하기 — DOCKER-USER 체인 삽질기

삽질기#GitLab#Docker#iptables#DOCKER-USER#방화벽

GitLab 운영하다가 특정 IP만 SSH/HTTPS로 git clone 할 수 있게 해야 한다는 요구가 들어옴. 그렇게 알고 준비하는데 부장님이 clone만 막는 게 아니라 아예 접근 자체를 못 하게 하라고 하심. 그럼 애플리케이션 레벨이 아니라 방화벽에서 잘라야 함.

INPUT 체인만으로 안 되는 이유

GitLab을 Docker로 돌리고 있었는데, 여기가 함정임. 평소 하던 대로 iptables INPUT 체인에 규칙을 넣어도 컨테이너로 가는 트래픽은 안 막힘. Docker가 포트포워딩을 NAT(FORWARD 경로)로 처리해서 INPUT 체인을 안 타기 때문.

찾아보니 Docker가 이런 용도로 DOCKER-USER라는 체인을 따로 만들어둠. 컨테이너로 가는 트래픽을 막으려면 여기에 규칙을 넣어야 함. 이거 알아내기까지 한참 삽질함.

정전과 사라진 규칙

규칙 넣고 잘 쓰고 있었는데, 금요일에 건물 전기공사가 있어서 정전된다는 공지가 내려옴. UPS까지 준비해놨는데도 서버가 꺼짐. 다시 켜지고 Docker가 재시작되니까 iptables 규칙이 싹 날아가 있었음. iptables는 원래 재부팅하면 휘발되는 데다 Docker가 자기 규칙을 다시 쓰면서 정리해버림.

거기에 “이 포트 열어달라”, “이 IP 추가해달라” 요청도 계속 들어옴. 매번 손으로 명령어 치는 건 답이 없어서 스크립트로 정리하기로 함.

스크립트

허용 IP/포트를 배열로 관리하고, INPUT과 DOCKER-USER 양쪽에 같이 넣는 구조. IP는 예시로 바꿔둠.

#!/bin/bash

# 허용할 IP 대역 및 개별 IP
ALLOWED_IPS=("192.168.0.0/16" "203.0.113.10" "203.0.113.20" "198.51.100.30" "172.17.0.0/16")

# 기본적으로 허용할 포트 리스트
ALLOWED_PORTS=(22 80 443)

# 추가 포트 (내부망만 허용)
EXTRA_PORTS=(8000 9000 9433)

echo "▶ Flushing existing rules..."
iptables -F INPUT
iptables -F DOCKER-USER

echo "▶ Setting ACCEPT rules for allowed IPs and ports..."
for IP in "${ALLOWED_IPS[@]}"; do
  for PORT in "${ALLOWED_PORTS[@]}"; do
    iptables -A INPUT       -p tcp --dport "$PORT" -s "$IP" -j ACCEPT
    iptables -I DOCKER-USER -p tcp --dport "$PORT" -s "$IP" -j ACCEPT
  done
done

# EXTRA_PORTS는 내부망만 허용
for PORT in "${EXTRA_PORTS[@]}"; do
  iptables -A INPUT       -p tcp --dport "$PORT" -s "192.168.0.0/16" -j ACCEPT
  iptables -I DOCKER-USER -p tcp --dport "$PORT" -s "192.168.0.0/16" -j ACCEPT
done

echo "▶ Dropping everything else for specified ports..."
for PORT in "${ALLOWED_PORTS[@]}"; do
  iptables -A INPUT       -p tcp --dport "$PORT" -j DROP
  iptables -A DOCKER-USER -p tcp --dport "$PORT" -j DROP
done

for PORT in "${EXTRA_PORTS[@]}"; do
  iptables -A INPUT       -p tcp --dport "$PORT" -j DROP
  iptables -A DOCKER-USER -p tcp --dport "$PORT" -j DROP
done

# 기타 패킷은 RETURN 처리 (DOCKER-USER만)
iptables -A DOCKER-USER -j RETURN

echo "▶ Current iptables rules:"
echo "[ INPUT ]"
iptables -L INPUT -v -n --line-numbers
echo
echo "[ DOCKER-USER ]"
iptables -L DOCKER-USER -v -n --line-numbers

echo "Done!"

포인트만 짚으면:

  • INPUT + DOCKER-USER 양쪽에 규칙을 넣음. 호스트로 오는 건 INPUT, 컨테이너로 가는 건 DOCKER-USER가 잡음.
  • 허용 IP를 먼저 ACCEPT 하고, 그 포트로 오는 나머지는 전부 DROP.
  • 172.17.0.0/16은 도커 내부 네트워크 대역이라 허용 목록에 포함.
  • 마지막에 DOCKER-USER -j RETURN으로 나머지 트래픽은 도커 기본 체인으로 돌려보냄.

재부팅 대응

정전 사태 재발 방지로 크론 @reboot에 등록함.

sudo crontab -e
@reboot /root/scripts/gitlab-firewall.sh

이러면 서버가 꺼졌다 켜져도 규칙이 자동으로 다시 들어감. 이후로 IP 추가 요청이 오면 스크립트의 ALLOWED_IPS 배열에 한 줄 추가하고 다시 실행하면 끝. 잘 돌아감.

정리

  • Docker 컨테이너 트래픽은 INPUT 체인으로 안 막힘. DOCKER-USER 체인에 넣어야 함.
  • iptables 규칙은 재부팅·도커 재시작에 날아감. 스크립트로 만들어서 @reboot에 걸어둘 것.
  • 허용 IP/포트를 배열로 빼두면 요청 올 때마다 한 줄 추가로 끝나서 운영이 편해짐.