[Cloudflare 딥다이브 EP.03] WireGuard로 홈·Vultr VPC를 하나의 사설망으로
Cloudflare Zero Trust + WireGuard 네이티브 조합으로 홈 네트워크와 Vultr VPC를 NAT 없이 하나의 사설망으로 묶는 실전 구축기다. macOS pf 방화벽 설정과 ipTIME/dnsmasq 라우팅 자동 배포까지 정리했다.
ㅂㄹㄱ
2026-04-14 ·
이 글은 개발자의 Cloudflare 인프라 딥다이브 시리즈의 세 번째 편이다.
EP.01~02에서 다룬 L2/L3 네트워킹과 Zero Trust 개념을 바탕으로 실제 구축 과정을 다룬다.
들어가며
집에서 쓰는 맥북으로 클라우드에 있는 서버에 사설 IP로 바로 SSH 접속하고 싶었다. 공인 IP 노출 없이, VPN처럼 번거롭지 않게, 마치 같은 LAN에 있는 것처럼 말이다. 로컬에서 작성한 코드를 VPC 서버의 DB에 직접 연결해 테스트해야 할 때마다 매번 SSH 터널링을 거는 것도 고역이었다.
카페든 출장지든 외부에서도 WARP 클라이언트 하나로 로그인하면 홈 네트워크와 클라우드 서버 모두에 접근할 수 있는 환경이 필요했다. 결론부터 말하면 Cloudflare Zero Trust + WireGuard 네이티브 조합으로 해결했다.
양쪽 게이트웨이 모두 wg-quick으로 WireGuard를 직접 돌려서 NAT 없이 원본 IP를 보존하는 구성이다. 외부에서는 Cloudflare WARP 클라이언트로 Zero Trust에 로그인하면 별도의 게이트웨이 없이 양쪽 프라이빗 네트워크에 바로 접근할 수 있다.
목표
- 홈 네트워크(10.10.0.0/16)에서 Vultr VPC(10.20.0.0/18) 내부 서버에 사설 IP로 직접 접근한다.
- 외부(카페, 출장지 등)에서 WARP 클라이언트 로그인 시 홈 네트워크와 VPC 모두 접근 가능하다.
- NAT 없이 원본 IP를 보존한다.
- 서버 추가 시 최소한의 설정으로 자동 연결한다.
- Cloudflare Zero Trust 기반으로 접근을 제어한다.
환경 구성
홈 네트워크
| 장비 | IP | 역할 |
|---|---|---|
| ipTIME 공유기 | 10.10.0.1 | 메인 라우터 (LAN /16), DHCP 서버 |
| Mac Mini | 10.10.20.1 | WireGuard 게이트웨이 (호스트 고정IP) |
| MacBook Pro | 10.10.2x.x | 클라이언트 디바이스 (호스트 고정IP) |
ipTIME 공유기의 LAN 서브넷을 /16(10.10.0.0/16)으로 설정해서 홈 전체를 하나의 L2 브로드캐스트 도메인으로 묶는다. 용도별 구분은 문서상의 운용 규약으로만 관리한다.
| 대역 | 용도 |
|---|---|
| 10.10.0.0/24 ~ 10.10.9.0/24 | 게스트 DHCP (10개 /24) |
| 10.10.20.0/24 ~ 10.10.29.0/24 | 호스트 고정IP (10개 /24, Mac Mini = 10.10.20.1) |
| 10.10.30.0/24 ~ 10.10.39.0/24 | IoT 장비 고정IP (10개 /24) |
| 나머지 | 확장용 예비 |
이렇게 하면 ipTIME LAN 마스크, Cloudflare Private Network, Split Tunnel, dnsmasq Option 121 등록이 전부 10.10.0.0/16 한 줄로 끝나고, 숫자 규칙(0~9=게스트, 20~29=호스트, 30~39=IoT)은 IP만 봐도 용도를 파악할 수 있게 해준다. 실제 트래픽 격리가 필요해지면 VLAN을 도입하면 되지만, 이 시리즈에서는 ipTIME으로 끝까지 간다.
Mac Mini(10.10.20.1)에서 홈 디바이스로의 패킷 전달은 같은 /16 안이라 ipTIME의 라우팅이 아닌 스위칭 기능만으로 처리된다.
Vultr VPC (ICN 리전)
| 서버 | VPC IP | 역할 |
|---|---|---|
| 서버 A | 10.20.0.3 | WireGuard 게이트웨이 (라우터) |
| 서버 B | 10.20.0.5 | 서비스용 인스턴스 |
Vultr VPC는 순수 L2 네트워크다. 별도의 게이트웨이나 라우터 없이 스위치만 있는 구조이므로, 각 VM이 직접 라우팅을 수행해야 한다.
전체 아키텍처
graph LR
subgraph Home["홈 네트워크 (10.10.0.0/16)"]
MB[MacBook] --- IP[ipTIME]
IP --- MM[Mac Mini Gateway]
end
subgraph CF["Cloudflare Zero Trust"]
Backbone((Cloudflare Backbone))
end
subgraph Vultr["Vultr VPC (10.20.0.0/18)"]
SVA[서버 A Gateway] --- SVB[서버 B]
end
subgraph Remote["외부 환경"]
WARP[WARP Client]
end
MM <== WireGuard ==> Backbone
SVA <== WireGuard ==> Backbone
WARP -. 로그인 .- Backbone
Backbone ==> SVA
Backbone ==> MM
외부에서 WARP 클라이언트로 Zero Trust에 로그인하면 Cloudflare 백본에 직접 연결된다. 이 상태에서 Private Network으로 등록된 모든 대역에 접근할 수 있다. 양쪽 게이트웨이는 WireGuard 네이티브(wg-quick)로 구성한다. 커널 레벨 터널이라 포워딩된 패킷도 정상적으로 처리하며, NAT 없이 양방향 통신이 가능하다.
1. Cloudflare Zero Trust 대시보드 설정
1.1 WARP Connector 생성
WARP Connector는 홈용(Mac Mini)과 VPC용(서버 A)으로 각각 하나씩 총 두 개를 생성한다. Networks → Connectors 메뉴에서 생성하고, eyJhIjoi로 시작하는 토큰을 복사해 둔다. 이 토큰은 나중에 설정을 추출할 때 사용한다.
1.2 디바이스 프로필 설정
Settings → WARP Client → Device settings에서 WARP Connector 전용 프로필을 만든다.
- 조건을
warp_connector@<team-name>.cloudflareaccess.com으로 설정한다. - Device tunnel protocol을 WireGuard로 변경한다. 기본값인 MASQUE 상태에서는 설정을 추출할 수 없으므로 반드시 변경해야 한다.
1.3 Private Network 및 Split Tunnel
각 터널의 Private Network 탭에서 해당 사이트의 CIDR을 등록한다. 서버 A 터널에는 10.20.0.0/18을, Mac Mini 터널에는 10.10.0.0/16을 등록한다.
또한 Split Tunnels 설정에서 Include 모드를 선택하고 10.20.0.0/18, 10.10.0.0/16, 100.96.0.0/12 대역을 추가한다.
2. WireGuard 설정 추출 (wgcf-connector)
공식적으로 WireGuard 설정 파일을 제공하지 않으므로, wgcf-connector 도구를 사용해 토큰에서 설정을 추출한다.
docker run --rm -v $(pwd):/app/output ghcr.io/animmouse/wgcf-connector <복사한-토큰>
추출된 wg0.conf 파일에서 AllowedIPs를 수정해야 한다. 0.0.0.0/0으로 두면 해당 장비의 모든 트래픽이 터널을 타게 되어 SSH 연결이 끊길 수 있다. 반드시 필요한 대역만 명시하도록 수정한다.
3. 게이트웨이 구성 (Linux/macOS)
3.1 서버 A (Linux)
명령어를 보기 전에 두 인터페이스의 정체를 짚고 가자. 앞으로 자주 등장한다.
enp8s0은 Vultr 인스턴스의 VPC 쪽 물리 NIC다. systemd의 Predictable Network Interface Names 규칙(enp= Ethernet,p8= PCI 버스 8번,s0= 슬롯 0)을 따른다. Vultr 인스턴스 종류와 생성 시점에 따라 이름이 다를 수 있으니ip addr명령으로 자기 환경의 VPC 인터페이스 이름을 먼저 확인하고 아래 명령의enp8s0을 그것으로 치환한다.wg0은 WireGuard가 만드는 가상 터널 인터페이스다.wg-quick up wg0명령은/etc/wireguard/wg0.conf를 읽어 동일 이름의 인터페이스를 생성한다. 이 인터페이스로 들어간 패킷은 자동으로 암호화되어 WG 피어로 전달된다.
WireGuard를 설치하고 추출한 설정을 /etc/wireguard/wg0.conf에 저장한 뒤 터널을 띄운다. 배포판에 맞춰 패키지를 설치한다.
sudo apt install wireguard
sudo dnf install wireguard-tools
sudo pacman -S wireguard-tools
brew install wireguard-tools
설치 후 터널을 기동한다. /etc/wireguard/wg0.conf를 읽어 wg0 인터페이스가 만들어진다.
sudo wg-quick up wg0
IP 포워딩을 활성화하고 iptables에서 포워딩을 허용한다.
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf # 재부팅 후에도 포워딩 유지
sudo sysctl -p # sysctl 설정을 즉시 커널에 반영
sudo iptables -A FORWARD -i enp8s0 -o wg0 -j ACCEPT # VPC → WG 방향(홈/외부 → CF) 허용
sudo iptables -A FORWARD -i wg0 -o enp8s0 -j ACCEPT # WG → VPC 방향(CF → 홈/외부 응답) 허용
3.2 Mac Mini (macOS)
macOS에서는 인터페이스 이름이 리눅스와 다르다.
en0은 Mac의 물리 NIC(이더넷 또는 첫 번째 활성 NIC).ifconfig나 시스템 환경설정에서 자기 장비의 이름을 확인한다. Wi-Fi가 주 인터페이스라면 보통en1이다.utun7은 macOS가 WireGuard에 할당한 터널 인터페이스. 리눅스의wg0에 해당하지만, macOS는 모든 터널을utunN형태로 자동 명명한다. 번호는 기존 터널 수에 따라 달라지므로wg-quick up직후ifconfig | grep utun으로 실제 번호를 확인해야 한다.
위의 탭 블록에서 macOS용 명령으로 wireguard-tools를 설치했다고 가정하고 터널을 띄운다. macOS는 conf 경로를 직접 지정하는 패턴을 쓴다.
sudo wg-quick up /etc/wireguard/wg0.conf # conf 경로 직접 지정 (macOS 패턴)
macOS의 pf(Packet Filter) 방화벽은 기본적으로 포워딩을 차단하므로 /etc/pf.conf에 허용 룰을 추가해야 한다. utun7은 자기 환경의 실제 번호로 치환한다.
# /etc/pf.conf에 추가
pass in on en0 all # 물리 NIC로 들어오는 패킷 허용 (홈 디바이스 → Mac Mini)
pass out on utun7 all # WG 터널로 나가는 패킷 허용 (Mac Mini → CF/VPC)
pass in on utun7 all # WG 터널에서 들어오는 패킷 허용 (CF/VPC → Mac Mini)
pass out on en0 all # 물리 NIC로 나가는 패킷 허용 (Mac Mini → 홈 디바이스 응답)
룰을 추가한 뒤 sudo pfctl -f /etc/pf.conf 명령으로 적용한다.
4. 라우팅 배포
양쪽 네트워크가 서로의 대역으로 가는 길을 알아야 한다. 홈과 VPC는 구조가 달라서 방식도 다르다.
4.1 홈 네트워크: ipTIME 정적 라우팅
홈 디바이스들은 기본 게이트웨이가 ipTIME이라, VPC 대역으로 나가는 패킷이 ipTIME에서 Mac Mini로 다시 한 홉 넘어가야 한다. ipTIME 관리 페이지 → "고급 설정 → 라우팅 관리"에서 다음을 등록한다.
| 대상 네트워크 | 서브넷 마스크 | 게이트웨이 | 용도 |
|---|---|---|---|
| 10.20.0.0 | 255.255.0.0 (/16) | 10.10.20.1 | Vultr 전체 |
| 10.30.0.0 | 255.255.0.0 (/16) | 10.10.20.1 | AWS 전체 |
| 10.40.0.0 | 255.255.0.0 (/16) | 10.10.20.1 | GCP 전체 |
| 10.50.0.0 | 255.255.0.0 (/16) | 10.10.20.1 | Oracle 전체 |
| 100.96.0.0 | 255.240.0.0 (/12) | 10.10.20.1 | WARP IP 대역 |
ipTIME 저가 모델은 정적 라우팅 UI가 없을 수 있으므로 관리 페이지에 메뉴가 있는지 먼저 확인한다. 홈 DHCP는 ipTIME 내장 기능을 그대로 쓰므로 홈에는 dnsmasq를 설치하지 않는다.
4.2 VPC 네트워크: dnsmasq DHCP Option 121
Vultr VPC는 순수 L2라 자체 DHCP/라우팅 배포가 없다. 서버 A에 dnsmasq를 설치하고 DHCP Option 121을 써서 VPC 내 다른 서버들에게 홈 대역과 WARP 대역 경로를 푸시한다.
# /etc/dnsmasq.d/vpc.conf
interface=enp8s0 # VPC 쪽 NIC에서만 DHCP 응답 (서버 A의 VPC 인터페이스)
dhcp-option=121,10.10.0.0/16,10.20.0.3,100.96.0.0/12,10.20.0.3 # 홈(10.10/16)과 WARP(100.96/12) 대역을 서버 A(10.20.0.3) 경유로 라우팅
이렇게 설정하면 VPC 내의 서버들이 부팅될 때 홈 네트워크와 WARP 대역으로 가는 길을 자동으로 학습하게 된다. 서버마다 ip route add를 수동으로 치지 않아도 된다.
삽질 기록
cloudflare-warp 데몬의 한계
처음에는 공식 WARP 데몬을 사용하려 했다. 하지만 데몬은 유저스페이스에서 동작하며 로컬 트래픽만 터널에 넣어주는 특성이 있었다. 다른 서버에서 포워딩된 패킷은 무시해버려 게이트웨이로 쓸 수 없었다. 결국 커널 레벨에서 동작하는 WireGuard 네이티브 방식으로 전환하여 해결했다.
macOS pf 방화벽의 함정
IP 포워딩을 켰음에도 Mac Mini에서 패킷이 멈췄다. 원인은 macOS의 pf 방화벽이었다. 리눅스의 iptables처럼 명시적으로 포워딩 룰을 열어줘야 한다는 사실을 나중에야 깨달았다. 또한 pfctl -ef로 설정을 통째로 교체하면 Apple의 시스템 기본 룰이 날아가므로 반드시 기존 파일에 append해야 한다.
정리
구축된 네트워크의 세 가지 대표 경로를 흐름도로 정리하면 다음과 같다.
sequenceDiagram
autonumber
participant MB as 홈 맥북 (10.10.2x.x)
participant IP as ipTIME (10.10.0.1)
participant MM as Mac Mini GW (10.10.20.1)
participant CF as Cloudflare 백본
participant SA as 서버 A GW (10.20.0.3)
participant SB as VPC 서버 B (10.20.0.5)
participant WC as 외부 WARP Client
Note over MB,SB: 경로 1. 홈 맥북 → VPC 서버 B
MB->>IP: dst=10.20.0.5 (기본 게이트웨이로 송신)
IP->>MM: 정적 라우팅 10.20.0.0/16 → 10.10.20.1
MM->>CF: wg0 터널 (암호화)
CF->>SA: Private Network 매칭 → VPC 터널
SA->>SB: 10.20.0.0/18 내부 L2
Note over SB,MB: 경로 2. VPC 서버 B → 홈 맥북 (응답)
SB->>SA: dst=10.10.2x.x, DHCP Option 121로 학습한 경로
SA->>CF: wg0 터널
CF->>MM: Private Network 매칭 → 홈 터널
MM->>MB: 같은 /16, ARP로 MAC 찾아 L2 전달 (ipTIME은 투명 스위치)
Note over WC,SB: 경로 3. 외부 WARP Client → 어디든
WC->>CF: ZTA 로그인 + Split Tunnel 대역만 캡처
CF->>MM: 홈 대역 요청 → 홈 터널
CF->>SA: VPC 대역 요청 → VPC 터널
이제 기본적인 인프라 구축이 끝났다. 다음 편에서는 이 복잡한 게이트웨이 설정을 서버 배포 시마다 수동으로 하지 않도록, Vultr Startup Script로 자동화하는 방법을 알아보자.