iptables 2026.09.11 7 min read

서비스를 멈추지 않고 Linux 권한과 iptables를 적용한 방법

Ubuntu 10대의 실제 계정과 통신을 조사한 뒤 최소권한과 iptables를 적용했다. 자동 원복을 준비하고 허용·차단 요청을 각각 시험한 과정을 기록했다.

서비스가 이미 동작하고 있어 권한과 방화벽을 바꾸는 순서부터 신경 써야 했다. 규칙을 잘못 적용하면 SSH뿐 아니라 Swarm 오버레이, DB 복제, NFS, 모니터링도 끊길 수 있었다.

먼저 실제 통신과 계정 사용 현황을 모았다. 바꿀 항목과 영향을 받을 서비스를 표로 정리한 뒤 단계별로 적용했다.

정책서보다 현재 서비스를 먼저 봤다

문서에 적힌 포트와 실행 중인 프로세스가 사용하는 포트가 같다는 보장은 없었다. 계정과 수신 주소도 함께 확인해야 했다. Ubuntu 10대에서 아래 항목을 읽기 전용으로 수집했다.

  • 사용자·그룹·sudo 권한·docker 그룹 가입 현황
  • systemd 서비스의 User, Group, 실행 파일
  • 파일 소유자와 접근 모드
  • 수신 소켓과 접속 주체
  • Docker 공개 포트와 오버레이 포트
  • NFS export의 접근 허용 대상
  • MySQL 계정의 Host 조건과 exporter 접속 출발지
  • Tailscale 인터페이스와 관리 접속 경로

조사 결과를 서버 역할별 표로 만들고 변경 후 예상 상태를 먼저 공유했다. 검토와 승인이 끝난 뒤 계정, SSH, 방화벽 변경을 시작했다.

서비스 계정에는 로그인 권한을 주지 않았다

mysql, dbbackup, node_exporter, mysqld_exporter, alloy는 서비스 실행용 계정이다. 사람이 SSH로 접속할 필요가 없어 셸을 nologin 또는 /bin/false로 설정하고 sudo와 docker 그룹에서 제외했다.

보조 그룹도 각 서비스가 필요한 범위로 제한했다. Alloy에는 journald를 읽는 권한을 줬고, 백업 계정과 exporter 사이에는 검증에 필요한 최소 정보만 접근할 수 있게 했다.

App 컨테이너는 root 대신 10001:10001로 실행했다. NFS 디렉터리와 Secret 파일 권한도 이 UID가 필요한 작업을 수행할 수 있는 범위로 맞췄다.

UFW 대신 역할별 iptables를 선택했다

Docker는 자체 iptables 규칙을 만든다. 공개 포트로 들어오는 요청이 호스트의 일반 INPUT 경로와 다르게 처리될 수 있어 UFW 규칙만으로 접근을 제한하기는 어려웠다.

이 환경에서는 UFW를 비활성 상태로 두고 서버 역할에 맞는 별도 체인을 만들었다.

  • MEETBACK-IN: 호스트로 들어오는 요청을 필요한 범위만 허용
  • MEETBACK-DKR: Docker 공개 포트 접근 제어
  • DOCKER-USER → MEETBACK-DKR: Docker가 DNAT한 요청을 먼저 검사

Tailscale 관리 접속, 응답 패킷, 루프백과 필수 ICMP를 먼저 허용했다. Swarm Manager와 오버레이 포트는 클러스터 노드끼리만 사용할 수 있게 했다. MySQL과 NFS는 실제 사용 서버에서만, exporter는 모니터링 서버에서만 접근하도록 제한했다.

NPM의 외부 80/443은 포트 포워딩 대상 노드에서만 열었다. 관리자 포트는 Tailscale 접속만 허용하고, 등록하지 않은 Docker 공개 포트는 기본 차단했다.

Tailscale을 먼저 살려 둔 이유

서비스망의 SSH가 끊겼을 때도 원복할 접속 경로는 필요했다. 그래서 tailscale0으로 들어오는 관리 대역과 UDP 41641을 먼저 허용하도록 규칙을 배치했다.

SSH 설정을 바꾼 뒤에는 기존 연결이 유지되는지만 보지 않았다. 새 Tailscale 세션을 열고 서비스망의 자동화 접속도 따로 시험했다. PermitRootLogin no, 빈 비밀번호 금지, 키·비밀번호 설정을 확인하면서 운영에 필요한 접속이 사라지지 않게 순서를 잡았다.

적용 전에 자동 원복을 준비했다

각 호스트의 기존 IPv4·IPv6 규칙을 별도 경로에 저장했다. 변경 후 일정 시간이 지나면 이전 규칙을 복구하는 작업을 걸어 둔 상태에서 새 정책을 적용했다. 새 SSH 접속과 주요 서비스 점검이 통과하면 자동 원복 작업을 해제했다.

재부팅 후에도 방화벽이 적용되도록 systemd 서비스로 등록했다. 최종 점검에서 10대 모두 서비스가 active/enabled였고, IPv4·IPv6 체인과 연결 규칙, 원복용 백업이 있는지 확인했다.

허용뿐 아니라 차단도 시험했다

허용한 통신이 성공하는 것과 막아 둔 통신이 거부되는 것을 함께 확인했다. 사전에 만든 허용·차단 표를 기준으로 시험했다.

허용 예시는 다음과 같다.

  • Monitoring → node exporter 10대
  • Monitoring → cAdvisor와 MySQL exporter
  • Worker → MySQL Primary
  • Worker → App NFS
  • NFS Alloy → Loki
  • Tailscale → NPM admin

차단 예시는 다음과 같다.

  • Monitoring → MySQL 서비스 포트
  • Manager → NFS export
  • 일반 서비스 노드 → NPM 관리자 포트
  • 등록하지 않은 Docker 공개 포트

차단 시험은 해당 서비스에 접속할 필요가 없는 서버에서 진행했다. 운영 요청은 유지한 채 출발지에 따라 허용 결과가 달라지는지 확인했다.

최종 결과와 남은 운영 과제

Ubuntu 10대의 계정·SSH·iptables 설정이 역할별 표와 일치했다. 서비스 계정이 특권 그룹에 가입한 사례는 0건이었다. 변경 후에도 Tailscale 관리 접속과 Swarm, DB, NFS, 모니터링, 기본·예비 외부 경로가 유지됐다.

이 규칙은 현재 서버 구성에 맞춰 작성했다. Worker를 추가하거나 exporter 포트를 바꾸고 Docker 포트를 새로 공개하면 통신 표와 방화벽도 같이 수정해야 한다. 정기 점검과 변경 절차를 후속 운영 과제로 남겼다.

프로젝트에서 겪은 오류와 남은 개선 항목은 별도의 트러블슈팅 회고에 정리했다.

실제 검증 화면

  1. master1에서 호스트 INPUT과 Docker 공개 포트 규칙을 함께 확인했다. Tailscale, Swarm, 모니터링과 외부 포트의 허용 출발지를 나눴다.
  2. DOCKER-USERMEETBACK-DKR에 연결했다. 외부로 허용한 80·443 외의 관리·서비스 포트가 차단되는지 확인했다.
  3. DB1은 Worker와 Replica의 MySQL 접속, 모니터링 서버의 exporter 접속, Tailscale 관리 접속을 허용했다.
  4. NFS1의 2049는 두 Worker에만, node exporter는 모니터링 서버에만 허용했다.
    Swarm 관리자 노드의 INPUT 허용 규칙과 Docker 게시 포트 접근 제어 규칙 조회 결과

DOCKER-USER 체인에서 관리 포트 접근을 제한하고 HTTP 80·HTTPS 443 포트를 허용한 규칙

MySQL 3306 포트와 모니터링 9100·9104 포트의 접속 출발지를 제한한 DB 서버 방화벽 규칙

Worker의 NFS 2049 포트 접속과 모니터링 서버의 9100 포트 접속을 허용한 방화벽 규칙

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 결과 이동