2026.02.10인프라/DevOps
securitytailscalevpnufwhome-serverdocker

홈서버 Tailscale VPN 도입기: 열린 포트를 잠그고 보안을 강화하는 과정

문제: 방화벽 없이 운영되던 홈서버

VPN 네트워크 보안

홈서버를 운영하면서 가장 간과하기 쉬운 부분이 바로 방화벽 설정입니다. Ubuntu 서버를 처음 세팅할 때 UFW(Uncomplicated Firewall)가 기본적으로 비활성화되어 있다는 사실을 알고 계셨나요? 저의 홈서버도 마찬가지였습니다. ufw status를 확인해보니 inactive — 모든 포트가 외부에 그대로 노출된 상태로 운영되고 있었습니다.

SSH(22), 각종 웹 서비스, DB 포트, Redis, 내부 API 서버까지. 공유기 포트포워딩으로 연결된 서비스들이 방화벽 없이 인터넷에 열려 있었던 셈입니다.

목표: Zero Trust 네트워크 구성

보안 전환의 핵심 목표는 명확했습니다.

  • 관리 접근(SSH, DB, 내부 API): Tailscale VPN을 통해서만 접근 가능
  • 외부 공개 서비스: 웹(80/443)과 미디어 서버(Plex 32400)만 허용
  • 기존 서비스 무중단: Nginx 리버스 프록시와 PM2로 관리되는 서비스에 영향 없이 전환

Tailscale이란?

Tailscale은 WireGuard 기반의 메시 VPN 서비스입니다. 기존 VPN과 달리 별도의 VPN 서버를 구축할 필요 없이, 각 기기에 클라이언트를 설치하고 같은 계정으로 로그인하면 끝입니다.

bash
# Ubuntu 서버에 Tailscale 설치
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale status

설치 후 각 기기에 100.x.x.x 대역의 고정 IP가 부여됩니다. 이 IP는 Tailscale 네트워크 내에서만 유효하므로, 외부에서는 절대 접근할 수 없습니다.

가장 큰 장점은 기존 네트워크에 영향을 주지 않는다는 점입니다. Tailscale은 tailscale0이라는 별도의 가상 네트워크 인터페이스를 생성합니다. 기존 eth0이나 eno1의 트래픽은 전혀 건드리지 않으므로, 설치만으로는 기존 Nginx나 PM2 서비스에 아무런 영향이 없습니다.

8단계 보안 전환 과정

Phase 1-2: Tailscale 설치 및 SSH 전환

모든 서버에 Tailscale을 설치한 후, SSH 접속을 Tailscale IP로 전환합니다.

bash
# 기존 SSH 접속 (공인 IP 또는 내부 IP)
ssh user@192.168.x.x

Tailscale SSH 접속으로 전환

ssh user@100.x.x.x

이 단계에서 중요한 것은 SSH 접속이 확실히 되는지 검증한 후에 다음 단계로 넘어가는 것입니다. UFW를 활성화한 후 SSH 접속이 안 되면 물리적으로 서버에 접근해야 하기 때문입니다.

Phase 3-4: UFW 방화벽 활성화

bash
# 기본 정책: 모든 인바운드 차단
sudo ufw default deny incoming
sudo ufw default allow outgoing

Tailscale 서브넷 허용 (관리용)

sudo ufw allow from 100.64.0.0/10

내부망 허용 (LAN 기기 간 통신)

sudo ufw allow from [INTERNAL_IP]/16

외부 공개 포트만 허용

sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS sudo ufw allow 32400/tcp # Plex

방화벽 활성화

sudo ufw enable

100.64.0.0/10은 Tailscale이 사용하는 CGNAT 대역입니다. 이 규칙 하나로 Tailscale을 통한 모든 관리 접근(SSH, DB 접속 등)이 허용됩니다.

Phase 5: Docker 포트 바인딩 우회 문제 해결

이 단계가 가장 까다로웠습니다. Docker는 자체적으로 iptables 규칙을 추가하기 때문에 UFW 방화벽을 우회합니다.

예를 들어, Docker Compose에서 이렇게 포트를 매핑하면:

yaml
# 문제: UFW를 우회하여 외부에서 직접 접근 가능
ports:
  - "5432:5432"  # PostgreSQL
  - "6379:6379"  # Redis

UFW에서 5432 포트를 차단해도, Docker가 iptables에 직접 ACCEPT 규칙을 삽입하므로 외부에서 접근이 가능합니다.

해결 방법: 포트 바인딩을 localhost 또는 Tailscale IP로 제한합니다.

yaml
# 해결: 바인딩 주소를 명시적으로 제한
ports:
  - "127.0.0.1:5432:5432"  # localhost만 허용
  - "100.x.x.x:6379:6379"  # Tailscale IP만 허용

또는 포트 매핑 자체를 제거하고 Docker 네트워크 내부에서만 통신하도록 변경합니다.

yaml
# Docker 네트워크 내부 통신만 사용
services:
  db:
    # ports 섹션 제거
    networks:
      - internal
  app:
    depends_on:
      - db
    networks:
      - internal

Phase 6-7: fail2ban 설치 및 보안 모니터링

bash
# fail2ban 설치 (SSH 브루트포스 방어)
sudo apt install fail2ban
sudo systemctl enable fail2ban

추가로 보안 모니터링 스크립트를 cron에 등록하여, 주요 설정 파일(.bashrc, crontab 등)의 해시값을 주기적으로 비교하고 변경이 감지되면 Telegram으로 알림을 보내도록 구성했습니다.

Phase 8: 전체 검증

모든 서비스가 정상 동작하는지 체계적으로 검증합니다.

bash
# 외부에서 접근 테스트
curl -I https://blog.heywook.com  # 200 OK (정상)
nmap -p 22 <공인IP>               # filtered (차단됨)
nmap -p 5432 <공인IP>             # filtered (차단됨)

Tailscale 내부에서 접근 테스트

ssh user@100.x.x.x # 접속 성공 psql -h 100.x.x.x # 접속 성공

전환 전후 비교

항목BeforeAfter
SSH공인 IP로 접근 가능Tailscale IP로만 접근
DB (PostgreSQL)외부 노출Docker 내부 또는 Tailscale만
Redis외부 노출localhost 바인딩
웹 서비스80/443 열림80/443만 허용 (동일)
기타 포트전부 열림전부 차단

주의할 점

1. Docker 포트 바인딩은 반드시 확인하세요

UFW만 설정하면 안전하다고 생각하기 쉽지만, Docker 컨테이너의 포트 매핑은 UFW를 우회합니다. docker ps로 열린 포트를 확인하고, 0.0.0.0으로 바인딩된 포트가 있다면 반드시 수정해야 합니다.

2. SSH 전환은 반드시 검증 후 방화벽을 활성화하세요

Tailscale IP로 SSH 접속이 확인되기 전에 UFW를 활성화하면, 원격 접속이 불가능해질 수 있습니다. 물리 접근이 불가능한 원격 서버라면 더욱 주의가 필요합니다.

3. 보안 모니터링 알림의 오탐에 대비하세요

보안 모니터링 스크립트가 crontab 변경을 감지하면 "해킹 시도" 경고를 보낼 수 있습니다. 본인이 직접 수정한 경우에도 알림이 오므로, 경고 발생 시 해시값을 갱신하는 절차를 마련해두면 불필요한 알림을 줄일 수 있습니다.

핵심 정리

  • Tailscale은 기존 네트워크에 영향 없이 설치할 수 있는 메시 VPN입니다
  • UFW + Tailscale 조합으로 관리 포트는 VPN 전용, 웹 서비스만 외부 공개하는 구성이 가능합니다
  • Docker의 포트 매핑은 UFW를 우회하므로, 바인딩 주소를 명시적으로 제한해야 합니다
  • 전환 작업은 SSH 접속 확인 → UFW 활성화 → Docker 포트 수정 순서로 진행하면 서비스 중단 없이 완료할 수 있습니다