# Cloudflare Tunnel로 홈서버 안전하게 외부 공개하기: 포트포워딩 없는 제로트러스트 접속
Table of Contents
“집 밖에서도 내 NAS에 있는 영화를 보고 싶은데, 포트포워딩은 무섭고 VPN은 귀찮아요.”
홈서버를 운영하다 보면 누구나 한 번쯤 겪는 고민입니다. Jellyfin으로 구축한 미디어 서버, Nextcloud로 만든 개인 클라우드, Home Assistant로 제어하는 스마트홈. 집에서는 잘 작동하는데, 밖에서 접속하려면 갑자기 복잡해집니다.
전통적인 방법은 공유기에서 포트포워딩을 설정하고, DDNS를 연결하고, SSL 인증서를 발급받는 것이었습니다. 하지만 이 방식은 집 IP를 전 세계에 공개하는 것과 같습니다. 봇들의 무차별 공격, 취약점 스캔, DDoS 공격에 고스란히 노출되죠.
Cloudflare Tunnel은 이 문제를 근본적으로 해결합니다. 포트를 열지 않아도, 집 IP를 노출하지 않아도, 안전하게 외부에서 홈서버에 접속할 수 있습니다.
Cloudflare Tunnel이 뭔가요?
쉽게 말해 역방향 VPN입니다.
일반적인 VPN은 외부에서 집으로 들어오는 터널을 만듭니다. 하지만 Cloudflare Tunnel은 반대로, 집에서 Cloudflare로 나가는 터널을 만듭니다.
┌─────────────────────────────────────────────────────────────────┐
│ 전통적인 포트포워딩 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 인터넷 ──────────────────────────→ 공유기 ────→ 홈서버 │
│ │ │ │
│ │ 공격자도 직접 접근 가능 │ 포트 열림 │
│ └──────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Tunnel │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 인터넷 ────→ Cloudflare ←──────────── 홈서버 (cloudflared) │
│ │ │ │ │
│ │ DDoS 방어, │ 포트 닫힘 │
│ │ WAF 보호 │ 아웃바운드만 │
│ └────────────┘ └──────────────────────┘
│ │
└─────────────────────────────────────────────────────────────────┘
홈서버에서 cloudflared라는 데몬이 Cloudflare 서버에 연결을 유지합니다. 외부 요청은 Cloudflare를 거쳐서 이 터널을 통해 들어옵니다. 공유기 입장에서는 들어오는 연결이 없으니 포트를 열 필요가 없습니다.
왜 포트포워딩보다 나은가요?
1. 집 IP 주소 숨김
포트포워딩을 하면 도메인의 DNS 레코드에 집 IP가 그대로 노출됩니다. 누구나 nslookup으로 확인할 수 있죠. Cloudflare Tunnel을 사용하면 DNS 레코드에는 Cloudflare의 IP만 보입니다.
2. DDoS 방어 기본 제공
Cloudflare는 세계 최대의 DDoS 방어 네트워크를 운영합니다. 무료 플랜에서도 기본적인 DDoS 방어가 제공됩니다. 홈서버를 직접 노출했다면 공유기가 감당해야 할 트래픽을 Cloudflare가 대신 막아줍니다.
3. 제로 인바운드 포트
공유기에서 어떤 포트도 열 필요가 없습니다. 이는 공격 표면(attack surface)을 획기적으로 줄입니다. 포트 스캔을 해도 아무것도 나오지 않습니다.
4. 무료 SSL 인증서
Let’s Encrypt 갱신 자동화를 신경 쓸 필요가 없습니다. Cloudflare가 알아서 처리합니다.
사전 준비
시작하기 전에 필요한 것들:
- Cloudflare 계정 (무료)
- 본인 소유의 도메인 (연간 $10 내외)
- Docker가 설치된 홈서버
도메인이 없다면 Cloudflare Registrar에서 직접 구매하거나, 다른 곳에서 구매 후 네임서버를 Cloudflare로 변경하면 됩니다.
1단계: Cloudflare에 도메인 추가
이미 도메인이 Cloudflare에 연결되어 있다면 이 단계를 건너뛰세요.
- Cloudflare Dashboard에 로그인
- “Add a Site” 클릭
- 도메인 입력 후 Free 플랜 선택
- 안내에 따라 네임서버 변경
네임서버 변경 후 전파까지 최대 24시간이 걸릴 수 있지만, 보통 몇 시간 내로 완료됩니다.
2단계: Tunnel 생성
Cloudflare Dashboard에서 터널을 생성합니다.
- 좌측 메뉴에서 Zero Trust 클릭
- Networks → Tunnels 이동
- Create a tunnel 클릭
- 이름 입력 (예:
home-server) - Docker 설치 방법 선택
- 표시되는 토큰을 복사해둡니다
토큰은 eyJhIjoiNzg...처럼 긴 문자열입니다. 이 토큰이 있어야 홈서버에서 Cloudflare에 연결할 수 있습니다.
3단계: Docker로 cloudflared 실행
홈서버에서 cloudflared 컨테이너를 실행합니다.
docker-compose.yml 방식 (권장)
# docker-compose.yml
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run
environment:
- TUNNEL_TOKEN=eyJhIjoiNzg...여기에_토큰_붙여넣기
networks:
- homelab
networks:
homelab:
external: true
# 실행
docker compose up -d
단일 명령어 방식
docker run -d \
--name cloudflared \
--restart unless-stopped \
cloudflare/cloudflared:latest \
tunnel --no-autoupdate run \
--token eyJhIjoiNzg...여기에_토큰_붙여넣기
실행 후 Cloudflare Dashboard에서 터널 상태가 HEALTHY로 표시되면 성공입니다.
4단계: 서비스 연결 (Public Hostname)
터널이 연결되면 이제 내부 서비스를 외부 도메인에 연결합니다.
Jellyfin 연결 예시
- Cloudflare Zero Trust → Networks → Tunnels
- 생성한 터널 클릭 → Public Hostname 탭
- Add a public hostname 클릭
Subdomain: jellyfin
Domain: yourdomain.com
Path: (비워둠)
Service:
Type: HTTP
URL: jellyfin:8096
핵심 포인트:
URL에는 Docker 컨테이너 이름과 내부 포트를 적습니다.localhost가 아니라 컨테이너 이름입니다.
같은 Docker 네트워크(homelab)에 Jellyfin이 있다면:
# jellyfin의 docker-compose.yml
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
# ... 기타 설정
networks:
- homelab
networks:
homelab:
external: true
이제 https://jellyfin.yourdomain.com으로 어디서나 접속할 수 있습니다.
여러 서비스 연결
jellyfin.yourdomain.com → jellyfin:8096
nextcloud.yourdomain.com → nextcloud:80
homeassistant.yourdomain.com → homeassistant:8123
portainer.yourdomain.com → portainer:9000
서비스마다 서브도메인을 다르게 설정하면 깔끔하게 관리할 수 있습니다.
5단계: 접근 제어 추가 (선택, 강력 추천)
지금까지의 설정만으로도 IP 노출 없이 외부 접속이 가능합니다. 하지만 URL을 아는 사람은 누구나 접속할 수 있죠.
Cloudflare Access를 사용하면 로그인한 사용자만 접속하도록 제한할 수 있습니다.
Access Application 생성
- Cloudflare Zero Trust → Access → Applications
- Add an application → Self-hosted 선택
- 설정:
Application name: Jellyfin
Session Duration: 24 hours
Application domain: jellyfin.yourdomain.com
Policy 설정
Policy name: Allow Family
Action: Allow
Include:
- Emails ending in: @gmail.com
또는
- Email: me@gmail.com, family@gmail.com
이렇게 설정하면 지정된 이메일로 로그인한 사용자만 접속할 수 있습니다. 로그인은 이메일로 전송되는 일회용 코드(OTP)로 진행됩니다.
서비스별 차등 적용
모든 서비스에 로그인을 요구하면 불편할 수 있습니다. 저는 이렇게 구분합니다:
| 서비스 | Access 적용 | 이유 |
|---|---|---|
| Jellyfin | X | 자체 로그인 있음, 가족 공유 |
| Nextcloud | X | 자체 로그인 있음 |
| Home Assistant | O | 민감한 스마트홈 제어 |
| Portainer | O | 서버 관리 도구 |
| 내부 관리 페이지 | O | 인증 없는 서비스들 |
주의사항과 팁
1. 서비스의 자체 인증을 끄지 마세요
Cloudflare Access를 적용했더라도, 서비스 자체의 로그인 기능은 유지하세요. 다중 보안 계층이 중요합니다.
2. WebSocket 설정
Home Assistant처럼 WebSocket을 사용하는 서비스는 추가 설정이 필요합니다:
- Public Hostname 설정에서 해당 호스트 클릭
- Additional application settings 확장
- HTTP Settings → WebSockets: On
3. 대용량 파일 업로드
Nextcloud로 큰 파일을 업로드하려면:
- Public Hostname 설정에서 해당 호스트 클릭
- Additional application settings 확장
- HTTP Settings → HTTP/2: On
Cloudflare 무료 플랜의 업로드 제한은 100MB입니다. 더 큰 파일은 Cloudflare를 거치지 않는 직접 연결이 필요합니다.
4. 로컬 접속 시 외부 경유 방지
집에서 jellyfin.yourdomain.com으로 접속하면 트래픽이 집 → Cloudflare → 집으로 돌아옵니다. 비효율적이죠.
Split DNS 설정으로 해결할 수 있습니다:
Pi-hole이나 AdGuard Home을 사용한다면:
# Local DNS Records
jellyfin.yourdomain.com → 192.168.1.100
nextcloud.yourdomain.com → 192.168.1.100
이렇게 하면 내부 네트워크에서는 로컬 IP로, 외부에서는 Cloudflare Tunnel로 접속합니다.
전체 구성 예시
최종적으로 제 홈서버 docker-compose는 이런 구조입니다:
# docker-compose.yml
services:
# Cloudflare Tunnel
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run
environment:
- TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
networks:
- homelab
# 미디어 서버
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- /mnt/media:/media:ro
networks:
- homelab
# 개인 클라우드
nextcloud:
image: nextcloud:latest
container_name: nextcloud
restart: unless-stopped
volumes:
- ./nextcloud:/var/www/html
environment:
- TRUSTED_PROXIES=cloudflared
- OVERWRITEPROTOCOL=https
networks:
- homelab
networks:
homelab:
driver: bridge
# .env
CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoiNzg...
마치며
Cloudflare Tunnel은 홈서버 외부 공개의 패러다임을 바꿉니다. 더 이상 포트포워딩, DDNS, SSL 인증서 갱신을 신경 쓸 필요가 없습니다. 공격자가 집 IP를 알 수도 없고, 알더라도 열린 포트가 없으니 공격할 수가 없습니다.
물론 만능은 아닙니다. Cloudflare 서비스에 의존하게 되고, 무료 플랜에는 업로드 용량 제한이 있습니다. 하지만 대부분의 홈서버 사용 케이스에서는 이 정도 제약은 문제가 되지 않습니다.
VPN(WireGuard, Tailscale 등)과 비교하면 접근성 측면에서 장단점이 있습니다. VPN은 클라이언트 설치가 필요하지만 더 넓은 범위의 접근(파일 공유, RDP 등)이 가능합니다. Cloudflare Tunnel은 웹 기반 서비스에 특화되어 있고 클라이언트 설치 없이 브라우저만으로 접속할 수 있습니다.
저는 둘을 병행합니다. 웹 서비스는 Cloudflare Tunnel로, 서버 관리나 파일 전송은 WireGuard VPN으로. 상황에 맞는 도구를 선택하시기 바랍니다.