공유기 포트 안 열고 집 서버에 SSH 들어가기 — Cloudflare Tunnel + ProxyCommand
집에 있는 Proxmox 호스트와 VM 4대에 외부에서 SSH로 들어가야 했는데, 공유기 포트는 절대 안 열고 싶었다.
목차
집에 있는 서버 한 대에 외부에서 SSH로 들어가는 건 ssh user@공인IP로 끝나지만, 그러려면 공유기에 포트 22를 열어둬야 한다. 그게 싫었다.
- 공인 IP가 KT 다이나믹이라 바뀜
- 22번 열어두면 매일 수천 건 무차별 대입
- 가족 공유 공유기라 설정 건드리기 부담
- VPN 셋업도 가족 PC에 깔게 강제할 수 없음
답은 Cloudflare Tunnel이었다. 무료, 포트 노출 0, 도메인 기반 식별.
결과물
ssh ssh.lab-it.net → 172.30.1.100 (Proxmox 호스트)
ssh vm1.lab-it.net → 172.30.1.110 (마인크래프트 서버)
ssh vm2.lab-it.net → 172.30.1.120 (Docker 서버)
ssh vm3.lab-it.net → 172.30.1.130 (Project/허니팟 서버)
학교에서, 카페에서, 어디서든 ssh vm3.lab-it.net 한 줄. 키 인증, 비밀번호 없음.
아키텍처
Mac (어디서든)
│
│ ssh vm3.lab-it.net
│
│ ProxyCommand: cloudflared access ssh --hostname vm3.lab-it.net
▼
Cloudflare 엣지 (TLS)
│
│ Cloudflare Tunnel (지속 연결)
▼
Proxmox 호스트 vm0 (172.30.1.100)
│ cloudflared 데몬
├─▶ 자기 자신 ssh (ssh.lab-it.net)
└─▶ 내부 네트워크 → vm1/vm2/vm3 ssh (vm*.lab-it.net)
핵심: 공유기에 포트를 열지 않는다. cloudflared가 outbound 연결을 Cloudflare 엣지에 유지하고, 외부 요청이 그 연결을 타고 들어온다.
Proxmox 호스트에 cloudflared 설치
# 패키지 설치
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
dpkg -i cloudflared.deb
# Cloudflare 계정 로그인 (브라우저 자동 열림)
cloudflared tunnel login
# 터널 생성
cloudflared tunnel create proxmox-ssh
# tunnel ID: bd6de2ac-2b70-42c2-9736-d8d63367a7f6
터널 생성하면 ~/.cloudflared/<tunnel-id>.json에 자격증명이 저장된다.
config.yml — 다중 호스트 라우팅
핵심은 하나의 터널에 여러 호스트네임을 라우팅하는 것이다. Cloudflare Tunnel은 ingress 규칙으로 호스트네임 → 내부 주소 매핑을 정의한다.
/etc/cloudflared/config.yml:
tunnel: bd6de2ac-2b70-42c2-9736-d8d63367a7f6
credentials-file: /root/.cloudflared/bd6de2ac-2b70-42c2-9736-d8d63367a7f6.json
ingress:
- hostname: ssh.lab-it.net
service: ssh://localhost:22
- hostname: vm1.lab-it.net
service: ssh://172.30.1.110:22
- hostname: vm2.lab-it.net
service: ssh://172.30.1.120:22
- hostname: vm3.lab-it.net
service: ssh://172.30.1.130:22
- service: http_status:404
마지막 catch-all http_status:404는 필수 — 빠지면 cloudflared가 시작 거부한다.
DNS 레코드
각 호스트네임이 터널을 가리키게 DNS CNAME을 만든다. CLI로 한 번에:
cloudflared tunnel route dns proxmox-ssh ssh.lab-it.net
cloudflared tunnel route dns proxmox-ssh vm1.lab-it.net
cloudflared tunnel route dns proxmox-ssh vm2.lab-it.net
cloudflared tunnel route dns proxmox-ssh vm3.lab-it.net
자동으로 <tunnel-id>.cfargotunnel.com을 가리키는 CNAME이 생긴다.
systemd로 상시 가동
cloudflared service install
systemctl enable --now cloudflared
호스트 부팅 시 자동 시작. cloudflared가 outbound 연결을 유지하므로 공유기 재부팅돼도 IP 바뀌어도 자동 복구.
Mac 쪽 — ProxyCommand
~/.ssh/config:
Host ssh.lab-it.net
HostName ssh.lab-it.net
User root
ProxyCommand cloudflared access ssh --hostname %h
IdentityFile ~/.ssh/id_ed25519
Host vm1.lab-it.net
HostName vm1.lab-it.net
User minecraft
ProxyCommand cloudflared access ssh --hostname %h
IdentityFile ~/.ssh/id_ed25519
Host vm2.lab-it.net
User docker
ProxyCommand cloudflared access ssh --hostname %h
IdentityFile ~/.ssh/id_ed25519
Host vm3.lab-it.net
User project
ProxyCommand cloudflared access ssh --hostname %h
IdentityFile ~/.ssh/id_ed25519
%h가 호스트네임으로 치환된다. Mac에 cloudflared CLI 설치는 brew install cloudflared.
각 VM의 ~/.ssh/authorized_keys에 Mac의 ed25519 공개키를 미리 등록해두면 비밀번호 없이 접속.
VM 간 라우팅 — 호스트가 게이트웨이
vm1/vm2/vm3는 Proxmox 내부 브리지(vmbr0)에 붙어 있고 외부에서 직접 접근 불가. 하지만 호스트(172.30.1.100)는 cloudflared와 같은 머신이므로, ingress 규칙에서 ssh://172.30.1.110:22처럼 직접 가리키면 호스트가 알아서 라우팅한다.
별도 NAT/iptables 없음. Proxmox가 만든 브리지가 알아서 처리.
보안
- 포트 22 노출 0. 외부에서 어떤 IP로도 22번에 직접 못 닿음.
- TLS 강제. Cloudflare 엣지~Mac 구간이 자동 TLS.
- Zero Trust 정책 (옵션). 대시보드에서 호스트네임마다 “내 이메일로 OTP 인증된 디바이스만”으로 추가 잠금 가능. 안 걸어도 키 인증이라 사실상 안전.
- fail2ban 의미 없음. 기존 22번 무차별 대입이 사라지므로 fail2ban 의존도가 떨어짐.
만들면서 알게 된 것들
http_status:404 catch-all 누락. 처음에 ingress 마지막 줄을 빼먹고 cloudflared가 안 떠서 한참 헤맴. yaml 검증을 강제하는 좋은 디자인.
Mac에 cloudflared 별도 설치 필요. Tunnel은 서버 측만 있다고 끝이 아니다. 클라이언트(access ssh)도 cloudflared가 깔려 있어야 ProxyCommand가 동작.
다이나믹 IP에 더 강함. KT 공인 IP가 새벽에 바뀌어도 cloudflared가 outbound라 자동 재연결. DDNS 셋업 안 해도 됨.
여러 호스트 한 터널. 처음엔 호스트마다 터널 따로 만들 줄 알았는데, 하나로 다중 호스트네임 라우팅이 정석. 관리 단순.
응용
같은 패턴으로 SSH뿐 아니라 모든 TCP/HTTP를 노출 가능:
ingress:
- hostname: honey.lab-it.net
service: http://172.30.1.130:8888 # 허니팟 대시보드
- hostname: xai.lab-it.net
service: http://172.30.1.120:8201 # XAI DDoS 백엔드
- hostname: ssh.lab-it.net
service: ssh://localhost:22
서비스 추가는 yaml 한 줄. 공유기는 평생 안 건드림.
홈랩에서 가장 답답한 건 NAT 뚫기다. Cloudflare Tunnel이 그 답답함을 영구히 제거한다.