TECH 으로 돌아가기
TECH HACKER NEWS 2026.10.31 22:08 9분 읽기 25 READS

I/O를 걷어낸 WireGuard, sanstun: Firezone이 Rust로 VPN 엔진을 새로 만든 이유

I/O를 걷어낸 WireGuard, sanstun: Firezone이 Rust로 VPN 엔진을 새로 만든 이유
SOURCE IMAGE · HACKER NEWS
I/O를 걷어낸 WireGuard, sanstun: Firezone이 Rust로 VPN 엔진을 새로 만든 이유

무슨 일이 있었나요?

제로 트러스트 원격 접속 솔루션을 만드는 Firezone이 sanstun이라는 Rust 기반 WireGuard 구현체를 공개했어요. 이름에 힌트가 있어요. 'sans'는 프랑스어로 '~없이'라는 뜻이고, 'tun'은 VPN이 쓰는 가상 네트워크 장치인 TUN에서 따온 말이에요. boringtun 같은 앞선 프로젝트 이름에도 들어 있죠. 그러니까 'I/O 없이(sans-IO) 동작하는 터널'이라는 뜻이고, 여기에 병렬 처리 친화적(parallel-friendly)이라는 수식어가 하나 더 붙었어요.

이게 왜 의미가 있냐면요. Rust로 사용자 공간 WireGuard를 구현할 때 가장 많이 쓰던 게 Cloudflare의 boringtun이었는데, 업데이트가 뜸해지면서 이걸 쓰던 회사들이 각자 포크해서 고쳐 쓰는 상황이 오래 이어졌거든요. Firezone도 그런 회사 중 하나였고, 실제 서비스를 운영하면서 부딪힌 문제를 구조부터 다시 풀겠다는 생각으로 새 구현을 내놓은 거예요.

먼저, WireGuard는 뭘까요?

WireGuard는 리눅스 커널에도 들어간 요즘 방식의 VPN 프로토콜이에요. 코드가 수천 줄 정도로 아주 작고, 암호 알고리즘을 그때그때 협상하지 않아요. Curve25519, ChaCha20-Poly1305, BLAKE2s 조합을 고정해서 쓰는 게 특징이죠. 동작은 크게 두 단계예요. 먼저 서로 키를 교환하는 핸드셰이크를 하고, 그다음부터는 만들어진 세션 키로 패킷을 하나씩 암호화해서 UDP로 주고받는 데이터 전송 단계가 이어져요. 패킷마다 카운터 번호가 붙는데, 이 번호가 암호화에 쓰는 nonce 역할도 하고, 같은 패킷을 다시 보내는 재전송 공격(replay attack)도 막아줘요.

sans-IO가 뭐냐면

sans-IO는 프로토콜 로직과 실제 입출력을 완전히 떼어놓는 설계 방식이에요. 요리사와 배달원을 나누는 거라고 생각하면 쉬워요. 요리사(프로토콜 상태 머신)는 재료(받은 바이트, 현재 시각)를 받으면 요리(보낼 바이트, 발생한 이벤트)를 내놓기만 해요. 그걸 소켓으로 보내거나 타이머를 거는 일은 배달원(애플리케이션)이 맡고요. 상태 머신 안에는 소켓도 없고, tokio 같은 async 런타임도 없고, 시스템 시계를 부르는 코드도 없어요.

이렇게 나눠두면 좋은 점이 꽤 많아요. 우선 테스트가 쉬워져요. 네트워크 없이도 '핸드셰이크 패킷을 넣고 시간을 2분 앞으로 돌리면 키를 다시 협상하는가' 같은 시나리오를 매번 같은 결과가 나오게(deterministic) 몇 밀리초 만에 돌려볼 수 있거든요. 또 같은 코어를 데스크톱, 모바일, 서버 어디에든 그대로 가져다 쓸 수 있어요. Firezone은 예전부터 자기 네트워킹 라이브러리를 이 방식으로 만들어온 걸로 알려져 있고, QUIC 구현체 quinn의 quinn-proto나 WebRTC 구현체 str0m도 같은 생각으로 만들어졌어요.

병렬 처리 친화적이라는 건?

사용자 공간 VPN에서 늘 문제가 되는 병목은 암호화예요. 기존 구현체들은 피어 하나의 터널 상태를 한 번에 한 스레드만 건드릴 수 있게 만든 경우가 많았어요. Rust로 말하면 패킷을 처리할 때마다 상태에 대한 가변 참조(&mut)가 필요해서 락을 잡고 순서를 기다려야 하는 거죠. 그래서 CPU 코어가 16개 있어도 한 연결의 트래픽은 사실상 코어 하나가 다 처리하게 돼요.

그런데 자세히 보면 데이터 전송 단계에서 정말로 함께 써야 하는 상태는 생각보다 적어요. 세션 키는 핸드셰이크가 끝나면 한동안 바뀌지 않으니 읽기만 하면 되고, 보내는 쪽은 패킷마다 겹치지 않는 카운터 번호만 받아가면 되니 원자적(atomic) 증가 연산으로 충분해요. 동기화가 필요한 건 받는 쪽의 재전송 방지 윈도우 정도예요. 이렇게 '자주 바뀌는 상태'와 '거의 안 바뀌는 상태'를 나누면 여러 스레드가 동시에 패킷을 암호화하고 복호화할 수 있어요. sanstun의 '병렬 친화'는 이런 방향을 노린다고 보면 돼요. 구체적인 API와 성능 수치는 원문에서 직접 확인해 보세요.

업계 맥락: 다들 WireGuard를 어떻게 돌리고 있을까

WireGuard 구현체는 크게 세 갈래로 나뉘어요. 첫째는 리눅스 커널 모듈이에요. 가장 빠르지만 커널에 묶여 있어서 모바일 앱이나 여러 플랫폼을 지원하는 제품에는 쓰기 어려워요. 둘째는 공식 Go 구현체인 wireguard-go예요. Tailscale이 여기에 GSO/GRO(여러 패킷을 묶어서 커널과 한 번에 주고받는 기법) 같은 최적화를 넣어 10Gbps가 넘는 속도를 낸 사례가 유명하죠. 셋째가 boringtun과 그 포크들이 있는 Rust 진영이에요.

sanstun은 세 번째 갈래에 속하면서 '더 빠르게'보다 '구조를 제대로'에 무게를 둔 시도예요. I/O가 분리돼 있으면 GSO 같은 플랫폼별 최적화나 io_uring 같은 새 I/O 방식도 코어를 건드리지 않고 바깥에서 붙일 수 있거든요. 처음엔 속도보다 구조를 택한 것처럼 보여도, 길게 보면 그 구조 덕분에 최적화를 계속 붙여나갈 수 있다는 판단이에요.

한국 개발자에게 주는 시사점

WireGuard를 직접 구현할 일은 거의 없겠지만, 이 글에서 얻을 건 VPN 얘기만이 아니에요. 결제 연동, IoT 기기 통신, 게임 서버 프로토콜처럼 상태를 가진 프로토콜을 다루고 있다면 sans-IO 패턴은 꼭 알아두면 좋아요. 비즈니스 로직 중간에 소켓 호출이랑 sleep이 섞여 있어서 테스트를 못 짜던 코드, 다들 한 번쯤 보셨잖아요? 상태 머신을 '입력을 넣으면 출력이 나오는' 순수 함수에 가깝게 만들면 그런 고생이 확 줄어요.

사내망 접속이나 원격 근무 인프라를 고민하는 팀이라면 Firezone, Tailscale, NetBird 같은 WireGuard 기반 제로 트러스트 도구가 안에서 어떻게 바뀌고 있는지 지켜보는 것도 도움이 돼요. Rust로 네트워크 프로그래밍을 공부하고 있다면 실전 코드베이스로 이만한 교재도 드물고요.

마무리

한 줄 정리: sanstun은 'I/O를 분리하고 함께 쓰는 상태를 최소화하면 테스트도 쉬워지고 멀티코어도 잘 쓸 수 있다'는 네트워크 설계 원칙을 WireGuard에 적용한 결과물이에요.

여러분 코드에도 소켓 호출과 비즈니스 로직이 뒤섞인 프로토콜 코드가 있나요? sans-IO로 리팩터링한다면 어디서부터 시작하고 싶으세요?


🔗 출처: Hacker News

SOURCE · HACKER NEWS
원문 전체 보기 → https://www.firezone.dev/blog/introducing-sanstun
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...