Coti Lab
WireGuard 논문과 Tailscale 메시 VPN 아키텍처
셀프호스팅

WireGuard 논문이 어떻게 Tailscale의 '설정 없는' 메시 VPN이 됐을까

Coti
8분

2017년 2월 27일, 샌디에이고에서 열린 NDSS(Network and Distributed System Security) 심포지엄에서 Jason A. Donenfeld는 “WireGuard: Next Generation Kernel Network Tunnel”이라는 논문을 발표했어요. 이 논문이 제안한 구현체는 코드베이스가 약 4천 줄에 불과할 만큼 작았고, 딱 두 피어를 잇는 point-to-point 터널 그 이상도 이하도 정의하지 않았어요. 그런데 지금 수백만 대의 기기가 별도 설정 없이 서로 알아서 연결되는 Tailscale 같은 메시 VPN 제품이 바로 이 단순한 설계 위에 서 있는데, 어떻게 두 점만 잇는 논문 하나가 전체가 그물처럼 얽힌 “설정 없는” 메시 네트워크로 확장될 수 있었을까요?

WireGuard 논문이 실제로 정의한 건 무엇일까?

WireGuard 논문이 정의한 것은 정확히 “피어 하나와 피어 하나 사이의 암호화된 터널”이며, 그 이상의 네트워크 토폴로지나 자동 탐색 메커니즘은 논문 범위 밖이에요. Donenfeld는 IPsec처럼 협상 프로토콜과 보안 연관(SA) 데이터베이스로 상태를 관리하는 대신, 리눅스 커널 안에서 직접 동작하는 약 4천 줄짜리 최소 구현을 제안했어요. 사용자 공간에서 패킷을 처리하는 OpenVPN과 달리 커널 레벨에서 처리하면 컨텍스트 스위칭과 메모리 복사 오버헤드가 줄어든다는 게 핵심 동기였고, 실제로 리누스 토르발스가 이 코드를 “예술 작품”이라 평했을 정도로 단순함 자체가 설계 철학이었어요. 다만 이 단순함에는 트레이드오프가 있는데, WireGuard는 딱 정의된 피어 쌍끼리만 통신할 뿐 “새 기기가 나타나면 자동으로 나머지 모두와 연결된다” 같은 개념 자체가 프로토콜에 없어요. 암호화 방식 자체의 세부 설계(Noise 프로토콜 프레임워크와 핸드셰이크 구조)는 같은 시리즈의 WireGuard Noise 프로토콜 리뷰에서 별도로 다뤘고, 이 글은 그 위에서 메시 네트워킹이 어떻게 응용되는지에 집중해요.

크립토키 라우팅은 왜 메시 확장의 출발점이 될까?

크립토키 라우팅(cryptokey routing)은 공개키 자체를 라우팅 테이블의 항목으로 취급해, 특정 공개키로 서명된 패킷만 그 피어에게 허용된 IP 대역(AllowedIPs)의 트래픽으로 인정하는 방식이에요. 이 구조 덕분에 “이 피어가 누구인가”라는 인증 문제와 “이 트래픽을 어디로 보낼 것인가”라는 라우팅 문제가 완전히 하나로 합쳐지고, 별도의 인증서 체계나 세션 협상 없이도 공개키 하나만 배포하면 새 피어를 즉시 신뢰할 수 있어요. 직접 WireGuard 서버를 구축해본 사람이라면 AllowedIPs 설정 한 줄이 곧 방화벽 규칙이자 라우팅 규칙이라는 걸 이미 체감했을 텐데, 바로 이 성질이 나중에 Tailscale이 자동으로 피어를 추가·제거할 수 있는 토대가 돼요. 크립토키 라우팅이 없었다면 메시 확장을 위해 별도의 PKI 인프라와 라우팅 프로토콜을 각각 얹어야 했을 텐데, WireGuard는 이 둘을 원래부터 하나로 묶어뒀던 셈이에요.

Tailscale은 이 point-to-point 설계를 어떻게 메시로 늘렸을까?

Tailscale은 WireGuard 프로토콜 자체는 전혀 바꾸지 않고, 그 앞단에 coordination server라는 제어 평면(control plane)을 추가해 피어 간 키 교환과 설정 배포를 자동화하는 방식으로 메시를 구현해요. 각 기기는 tailnet에 로그인하는 순간 자신의 WireGuard 공개키를 coordination server에 등록하고, 서버는 이 키들과 연결 후보 정보를 나머지 모든 기기에 뿌려주는 “공개키 배포판” 역할만 해요. 여기서 끝이 아니에요 — 실제 데이터 패킷은 coordination server를 전혀 거치지 않고, 표준 WireGuard 프로토콜 그대로 기기와 기기 사이에서 직접 암호화·복호화돼요. 기기가 N대라면 이론상 최대 N(N-1)/2개의 독립된 WireGuard 터널이 필요한 완전 메시가 만들어지는데, 관리자가 이 조합을 손으로 설정 파일에 적어 넣는 대신 coordination server가 필요한 순간마다 자동으로 피어 정보를 밀어넣어 주는 거예요. 결국 Tailscale이 한 일은 새로운 암호화 방식을 발명한 게 아니라, WireGuard 논문이 관리자의 몫으로 남겨둔 “피어를 어떻게 찾고 등록할 것인가”라는 질문에 자동화된 답을 붙인 것에 가까워요.

서로 다른 NAT 뒤의 두 기기는 어떻게 직접 만날까?

두 기기가 각각 다른 공유기 뒤 NAT 안에 있어도 Tailscale은 홀펀칭(hole punching)이라는 기법으로 대부분 직접 연결을 만들어내요. 원리는 의외로 단순한데, 상태 저장 방화벽은 “내부에서 먼저 나간 패킷의 응답만 허용”하는 규칙을 따르기 때문에 양쪽 기기가 거의 동시에 서로에게 UDP 패킷을 쏘면 각자의 방화벽이 상대방의 패킷을 “내가 먼저 보낸 요청에 대한 응답”으로 착각해 통과시켜줘요. 문제는 각 기기가 NAT 밖에서 자신이 어떤 공인 IP:포트로 보이는지 스스로 알 방법이 없다는 건데, 이건 STUN 서버에 “당신이 보기에 내 주소가 뭔가요?”라고 물어 응답 패킷의 발신지 주소를 그대로 돌려받는 방식으로 해결해요. 목적지마다 다른 포트를 할당하는 까다로운 NAT(endpoint-dependent NAT)를 만나면 STUN만으로는 부족한데, 이 경우 한쪽이 수백 개 포트에서 동시에 패킷을 뿌리고 다른 쪽이 그 범위를 무작위로 탐색하는 birthday paradox 기법까지 동원해요. coordination server는 이 모든 과정에서 데이터를 중계하지 않고, 양쪽이 서로의 후보 주소를 교환할 수 있게 안전한 사이드 채널만 제공한다는 점이 핵심이에요.

홀펀칭이 실패하면 어떻게 될까? DERP 릴레이의 역할

DERP(Detoured Encrypted Routing Protocol)는 직접 연결이 불가능할 때 암호화된 WireGuard 패킷을 그대로 중계해주는 HTTP 기반 릴레이 프로토콜이에요. Tailscale 공식 블로그에 따르면 모든 연결은 사실 처음부터 가장 가까운 DERP 서버를 거쳐 시작되고, 그 사이 백그라운드에서 로컬 LAN 주소, STUN으로 알아낸 WAN 주소, UPnP로 확보한 포트 등 가능한 모든 연결 후보를 동시에 탐색해요. ICE(Interactive Connectivity Establishment)와 비슷한 이 방식은 “일단 되는 걸 다 써보고 가장 빠른 경로를 고른다”는 전략인데, 직접 경로가 발견되면 이미 연결된 세션을 끊지 않고 수 초 안에 조용히 직접 경로로 갈아타요. 이중 NAT를 쓰는 CGNAT 환경이나 기업 방화벽처럼 양쪽 모두 까다로운 NAT일 경우 홀펀칭에 최대 수십 분이 걸릴 수 있어 이 구간에서는 DERP가 사실상 유일한 대안이 되는데, 그래도 DERP 서버는 목적지 공개키를 기준으로 이미 암호화된 페이로드를 전달만 할 뿐 복호화할 권한이 없어서 릴레이를 거치더라도 종단간 암호화는 깨지지 않아요. 대신 릴레이 구간에서는 지연 시간이 늘고 대역폭이 제한되기 때문에, 실제 체감 성능은 직접 연결로 업그레이드되느냐에 크게 좌우돼요.

결과적으로 “설정 없는” 메시는 무엇을 의미할까?

“설정 없는” 메시란 관리자가 피어 목록이나 AllowedIPs를 손으로 관리하지 않아도, 로그인만으로 tailnet에 속한 모든 기기가 필요할 때마다 서로 최적 경로를 찾아 자동으로 연결되는 상태를 뜻해요. Tailscale로 홈랩에 원격 접속해본 사람이라면 새 기기를 앱 로그인 한 번으로 tailnet에 추가하는 순간 기존 모든 기기와 곧바로 통신 가능해지는 경험을 했을 텐데, 그 이면에서는 coordination server의 키 배포, STUN 기반 홀펀칭, DERP 폴백이 한 번에 조합되고 있는 거예요. WireGuard 논문이 “점과 점을 잇는 최소한의 안전한 파이프”만 정의하고 나머지를 전부 구현자의 몫으로 남겨뒀기 때문에, 오히려 그 위에 서로 다른 자동화 계층(Tailscale의 coordination server, 혹은 오픈소스 대안인 Headscale)을 자유롭게 얹을 수 있는 여지가 생겼다는 점이 이 설계의 진짜 힘이에요. 결국 논문 하나가 그대로 제품이 된 게 아니라, 논문이 의도적으로 좁혀둔 범위 덕분에 그 바깥을 채우는 생태계가 자라날 수 있었던 셈이에요.

결론

WireGuard 논문은 애초에 메시 네트워크를 약속한 적이 없고, 딱 두 피어 사이의 단순하고 검증 가능한 터널만 제안했어요. Tailscale은 이 최소 설계를 건드리지 않은 채 coordination server로 키 배포를 자동화하고, STUN·홀펀칭·birthday paradox로 직접 연결을 최대한 뚫고, 그마저 실패하면 DERP 릴레이로 안전하게 우회하는 세 겹의 계층을 얹어 “설정 없는” 전체 메시를 완성했어요. 결국 처음 질문으로 돌아가면, 두 점만 잇는 논문이 그물망 전체로 확장될 수 있었던 이유는 논문이 스스로 아무것도 더 하지 않고 딱 그 자리에만 머물러줬기 때문이에요.

참고 자료

자주 묻는 질문

Q1. WireGuard 자체에도 메시 네트워크 기능이 있나요?

아니요, WireGuard 프로토콜 자체는 피어 하나와 피어 하나를 잇는 point-to-point 터널만 정의할 뿐 여러 피어를 자동으로 엮어주는 메시 기능은 없고, 관리자가 각 서버에 설정 파일을 일일이 작성해 피어 목록을 등록해야 해요.

Q2. Tailscale을 쓰면 실제로 어떤 프로토콜로 패킷이 오가나요?

기기와 기기 사이의 실제 데이터 패킷은 표준 WireGuard 프로토콜 그대로 암호화되어 오가고, Tailscale이 자동화하는 부분은 그 앞단의 키 교환과 피어 주소 탐색 과정이에요.

Q3. NAT 홀펀칭은 항상 성공하나요?

아니요, Tailscale 공식 블로그에 따르면 STUN과 birthday paradox 기법을 함께 써도 직접 연결 성공률은 약 90%대에 머물고, 나머지 경우는 DERP 릴레이 서버를 거치는 방식으로 대체돼요.

Q4. DERP 릴레이를 거치면 보안이 약해지나요?

아니요, DERP 서버는 목적지의 공개키를 기준으로 이미 WireGuard로 암호화된 페이로드를 그대로 전달만 할 뿐 복호화 권한이 없어서, 릴레이 구간에서도 종단간 암호화는 그대로 유지돼요.

Q5. coordination server가 죽으면 이미 연결된 기기들도 통신이 끊기나요?

아니요, 이미 맺어진 WireGuard 터널은 coordination server 없이도 계속 동작하지만, 새로운 연결을 맺거나 키를 갱신하거나 정책을 업데이트하는 작업은 coordination server가 복구될 때까지 불가능해요.

Q6. 기기가 N대일 때 WireGuard 터널은 몇 개나 만들어지나요?

완전 메시 구조라서 이론상 최대 N(N-1)/2개의 point-to-point 터널이 만들어질 수 있지만, 실제로는 서로 통신이 필요한 기기 쌍 사이에서만 온디맨드로 터널이 맺어져요.

C
Coti 백엔드 개발자
프로필

댓글

첫 번째 댓글을 남겨보세요!