1P by GN⁺ | ★ favorite | 댓글 1개
  • Wag는 WireGuard에 다중 요소 인증, 라우트 제한, 기기 등록을 추가하는 프로젝트로, MFA가 필요한 라우트와 항상 접근 가능한 공개 라우트를 구분할 수 있음
  • 신규 클라이언트 등록 API, 고가용성, 실시간 사용자 업데이트와 알림, Security Key·SSO·PAM·TOTP 등 여러 MFA 통합을 제공함
  • 서버 운영에는 IP 포워딩 활성화가 필요하며, 수동 실행 시 iptableslibpam 설치, iptables와 WireGuard 장치 관리를 위한 root 실행이 필요함
  • 관리는 웹 UI와 CLI로 가능하며, CLI는 start, registration, devices, users, webadmin 하위 명령으로 등록 토큰, 기기 잠금, MFA 초기화, 웹 관리자 계정을 다룸
  • 제약으로는 클라이언트당 하나의 AllowedIP만 지원하며, 주로 Linux 전용이고 Windows는 일부 작업을 거치면 동작할 수 있음

Wag가 추가하는 WireGuard 기능

  • Wag는 WireGuard에 MFA, 라우트 제한, 기기 등록을 추가함
  • 라우트는 MFA 인증이 필요한 경로와 항상 접근 가능한 공개 라우트로 나눠 정의할 수 있음
  • 신규 클라이언트 등록을 위한 쉬운 API를 제공함
  • 고가용성, 실시간 사용자 업데이트와 알림을 지원함
  • MFA 통합은 다음 방식을 포함함
    • Security Key
    • SSO
    • PAM
    • TOTP
  • 문서는 Documentation에서 제공됨

설치와 실행 조건

  • 서버에서 포워딩이 활성화되어야 함
    • IPv4는 net.ipv4.ip_forward=1 설정을 사용함
    • IPv6는 net.ipv6.conf.all.forwarding=1 등 관련 sysctl 설정을 사용함
  • Docker Compose 실행 예시는 wagvpn/wag:latest 이미지를 사용함
    • 관리 페이지 포트 예시는 4433/tcp
    • 공개 등록 페이지 포트 예시는 8081/tcp
    • WireGuard 포트 예시는 53230/udp
    • /dev/net/tun 장치를 컨테이너에 연결함
  • 수동 설치에는 iptableslibpam이 필요함
  • Wag는 iptablesWireGuard 장치를 관리하기 위해 root로 실행되어야 함
  • 바이너리 릴리스는 glibc 2.31+가 필요함
  • 소스 빌드는 go1.23.1npm이 필요함

관리 방식

  • 관리 UI를 활성화한 뒤 Wag를 설정하면 첫 번째 관리자를 생성하고, 비밀번호를 STDOUT으로 출력함
  • 이후 웹 UI에 로그인해 사용자를 관리할 수 있음
  • root 사용자는 CLI로 Wag 서버를 관리할 수 있음
  • CLI 형식은 wag subcommand [-options]
  • 지원 하위 명령은 다음과 같음
    • start: Wag 서버를 시작하며 데몬화하지 않음
    • registration: 등록 토큰 생성, 삭제, 목록 조회를 처리함
    • devices: WireGuard 기기 목록 조회, 삭제, 잠금, 잠금 해제, 활성 MFA 세션 조회를 처리함
    • users: 사용자 MFA 관리, 사용자 삭제, 계정 잠금, MFA 초기화를 처리함
    • webadmin: 웹 UI 관리자 사용자 추가, 삭제, 목록 조회, 계정 잠금과 해제를 처리함
    • version, firewall도 지원 명령에 포함됨

등록 토큰과 MFA 흐름

  • 새 기기 등록은 먼저 wag registration -add -username tester 같은 명령으로 등록 토큰을 생성함
  • 생성된 토큰을 공개 등록 엔드포인트에 전달하면 WireGuard 설정 응답을 받을 수 있음
  • 반환되는 설정에는 Interface, PrivateKey, Address, Peer, Endpoint, PublicKey, AllowedIPs, PersistentKeepAlive 같은 항목이 포함됨
  • 사용자는 서버의 VPN 주소로 접속해 2FA 코드를 입력함
  • 세션이 만료되기 전까지 지속되는 시간은 설정 파일에서 지정함

웹 관리 콘솔

  • 관리 콘솔에 로그인하려면 Webserver.Management.Enabledtrue로 설정해야 함
  • 콘솔에서 sudo ./wag webadmin -add -username <your_username> -password <your-password-here>로 웹 관리자 계정을 추가함
  • 이후 관리 리스닝 주소로 접속해 자격 증명을 입력함
  • 웹 인터페이스 자체는 관리자 사용자 추가를 할 수 없음
  • 관리 포털은 외부에 노출하지 않는 것이 권장되며, ListenAddress127.0.0.1 또는 localhost로 설정하고 SSH 포워딩으로 노출하는 방식을 권장함

주요 설정 항목

  • NumberProxies는 클라이언트 앞의 신뢰할 수 있는 리버스 프록시 수를 지정하며, Wag가 X-Forward-For를 반영해 클라이언트 IP를 파싱하게 함
  • Socket은 Wag 제어 소켓이며, 변경하면 같은 머신에서 여러 Wag 인스턴스를 실행할 수 있음
  • NAT는 마스커레이딩을 켜거나 끄며, 활성화하면 모든 트래픽이 VPN 서버에서 시작된 것처럼 보임
  • NATExcludeRangesNAT=true일 때 NAT에서 제외할 CIDR 범위를 지정함
  • ExposePorts는 VPN 서버의 포트를 클라이언트에 노출하며 iptables 규칙을 추가함
  • CheckUpdates는 기본값이 꺼져 있으며, 활성화하면 관리 UI가 새 Wag 버전 알림을 표시하고 api.github.com에 접근함
  • Acls는 그룹과 정책을 정의하지만 첫 실행 시에만 반영되며, 런타임 중에는 웹 UI로 편집함
  • Webserver는 공개 등록 엔드포인트, 터널 MFA 포털, 관리 포털 설정을 포함함
  • Wireguard는 장치 이름, 리스닝 포트, 개인 키, VPN이 담당하는 서브넷, MTU, DNS 서버를 설정함
  • Clustering은 클러스터 이름, etcd 클러스터 상태, 로그 레벨, witness 노드, 데이터베이스 위치, 클러스터 인증서 관련 설정을 포함함

ACL 정책 동작

  • Policies는 VPN이 캡처할 라우트와 Wag를 통과할 포트·프로토콜을 정의함
  • 규칙 적용은 서브넷 prefix 길이를 사용하며, 가장 구체적인 매치가 라우트 접근 수준을 결정함
  • 예를 들어 /16을 MFA로 정의하고 그 안의 특정 /32를 Allow로 정의하면, 더 구체적인 /32가 우선되어 MFA 없이 접근 가능함
  • 이 동작은 v6.0.0에서 변경되었으며, 이전에는 MFA 라우트가 항상 우선했음
  • 하나의 라우트에 여러 정책이 정의되면 정책이 합성되며, MFA 규칙이 우선함
  • 아직 릴리스되지 않은 버전부터 Deny 규칙으로 라우트 접근을 차단할 수 있음
  • 가장 구체적인 규칙은 새 규칙 “버킷”을 만들기 때문에, /32 버킷에 deny만 있으면 같은 /32의 다른 포트 접근도 허용되지 않을 수 있음

포트와 프로토콜 규칙

  • 서비스 접근은 포트와 프로토콜 규칙으로 정의할 수 있음
  • 지원되는 규칙 유형은 3가지임
    • Any: 별도 규칙이 없거나 any 키워드를 쓰면 모든 서비스와 포트 조합을 허용함
    • Single Service: 192.168.1.1 22/tcp 53/udp처럼 호스트의 특정 TCP·UDP 포트를 허용함
    • Ranges: 192.168.1.1 22-1024/tcp 23-53/any처럼 포트 범위를 지정함
  • 포트 범위는 낮은 포트를 먼저 써야 함
  • ICMP는 포트가 없으므로 1.1.1.1 icmp처럼 포트 없이 지정할 수 있음

제한 사항과 개발

  • Wag는 클라이언트당 하나의 AllowedIP만 지원함
  • 이 제한은 클라이언트에서 서버로 이어지는 구조에 적합함
  • 주로 Linux 전용이며, Windows는 일부 작업을 거치면 동작할 수 있음
  • 개발 모드에서는 터널로 들어오는 요청의 IP를 클라이언트 IP로 설정하기 위한 환경 변수를 사용할 수 있음
  • 테스트 예시는 internal/router에서 sudo go test -v .를 실행함
  • 외부 기여는 기능 추가나 버그 수정 시 가능한 경우 테스트를 작성하고 Pull Request를 열도록 안내함

댓글과 토론

Hacker News 의견들
  • 보기에는 좋아 보이지만 몇 가지가 걸림
    curl [http://public.server.address:8080/register_device?key=e83253...](<http://public.server.address/register_device/…;) 예시와 “서비스가 완전히 템플릿화된 응답을 반환한다”는 설명을 보면, 등록 과정에서 클라이언트가 개인 키를 만들고 공개 키를 서버에 보내는 게 아니라 서버가 개인 키를 만들어 클라이언트로 보내는 듯함
    게다가 예시가 HTTP라서, 사람들이 HTTP도 괜찮은 선택지라고 생각하지 않도록 최소한 그 부분은 바꾸는 게 좋겠음
    세션이 만료되면 클라이언트가 이를 알아차릴 방법이 있는지도 궁금함. 아니면 SSH 세션 같은 것이 그냥 멈추는 건가?
    Wi-Fi의 캡티브 포털 감지처럼 동작하는 WireGuard 클라이언트를 가끔 찾아봤는데, 이상적으로는 설정 파일에 persistentkeepalive 같은 한 줄을 추가해 URL을 가져오고 주기적으로 확인하는 방식이면 좋겠음. OK가 오면 정상, 응답이 없으면 네트워크 문제, Location 헤더가 오면 브라우저를 그 위치로 띄워 세션 재인증 등을 하게 하는 식임
    아직 그런 클라이언트는 못 찾았음

    • 등록 URL은 선택적으로 pubkey 매개변수도 받을 수 있어서, 서버가 개인 키를 생성하는 방식에 의존하지 않아도 됨. 문서가 부족해서 헷갈릴 만함
      마지막 질문에 답하면, 내가 쓰는 eBPF XDPPASS, DROP, REDIRECT만 가능함. 그래서 가장 쉬운 결과인 PASS/DROP으로 처리하고, 연결은 그냥 멈추게 됨
      다만 캡티브 포털 감지 페이지를 wag MFA 목록에 추가하면 감지는 직접 구성할 수 있고, 이후는 브라우저가 처리해줄 것임
      wag에서 가로채기나 프록시처럼 동작하는 기능은 구현할 생각이 없음. 그걸 하면 인증 만료나 로그아웃 처리는 좀 더 쉬워지겠지만 방향이 아님
    • 그런 기능은 정말 멋질 것 같고, 이 프로젝트 작성자가 고려해주면 좋겠음
    • 비슷한 서버를 만든 적이 있음. 장치별 클라이언트 인증서가 필요하고, 그걸로 로그인 페이지에 mTLS로 접속한 뒤 OIDC로 사용자를 인증하고 터널을 활성화하는 구조였는데, 어려운 부분은 클라이언트였음
      Mac용으로 Go 클라이언트를 하나 작성했고 Brew의 명령줄 wg를 사용해 키 생성도 처리했지만, 투박하고 sudo가 필요했음
      네트워크 권한을 쓰는 제대로 된 네이티브 앱이면 좋겠지만, 내 역량 밖임
  • 세션 관리 문제를 이미 다뤘거나 다룰 계획이 있는지 궁금함
    본질적으로 WireGuard 키는 영원한 세션 키와 같음
    WireGuard 전송 계층을 구현하는 소프트웨어가 제대로 된 VPN 서버 솔루션이라면 세션 관리도 구현해야 한다고 봄. 즉 서버와의 두 번째 채널을 통해 세션 키를 주기적으로 교체하고, 세션을 종료하고, IP 주소를 바꾸고, 새 경로를 설정하고, 필요하면 인증을 반복해야 함

    • 그런 용도라면 Firezone을 쓰겠음. 사용자가 플랫폼에 정기적으로 로그인하도록 강제하는 옵션이 있고, OIDC로 외부 ID 제공자와 결합하면 세션 관리에 아주 견고하고 단순한 해결책이 됨
    • wag 맥락에서 “영원한 세션 키”가 정확히 무슨 뜻인지는 잘 모르겠음
      WireGuard 키는 wag 서버와 통신할 수 있게 해주지만, 실제 세션은 사용자가 인증됐는지 여부를 담은 eBPF 맵으로 유지됨
      그래서 누군가 개인 키 자료를 훔쳐도 MFA로 제한된 경로에는 접근할 수 없음
    • GlobalProtect 같은 VPN 클라이언트를 WireGuard로 만든다면, 클라이언트별 영구 인증 키를 두고 그걸로 VPN 컨트롤러까지 초기 터널을 만든 뒤, 그 안에서 인증을 수행하고 별도의 세션 키를 받게 할 것 같음. 첫 번째 터널은 인증을 마치고 실제 세션 키를 받는 즉시 끊는 구조임
    • 주기적 세션 키 회전, 세션 종료, IP 주소 변경, 새 경로 구성, 재인증을 위한 두 번째 채널이라면 사실상 IPsec의 IKE 프로토콜 아닌가? 그냥 IPsec을 쓰면 되지 않나?
  • TOTP 코드 무차별 대입을 막고 있는지 궁금함. 예를 들어 속도 제한이나 재시도 횟수 제한 같은 것임
    코드를 빠르게 훑어봤는데 그런 처리를 찾지 못했음
    상상하는 시나리오는 누군가 브라우저에서 TOTP 입력 UI를 열고 개발자 도구를 띄운 뒤 가능한 모든 TOTP 코드를 반복 시도하는 경우임

    • TOTP 코드 무차별 대입 방어가 있음. 각 인증에는 사용자가 시도할 수 있는 횟수 제한이 있고, 이를 넘기면 계정이 잠기며 관리자가 잠금을 해제해야 함
      특히 장치가 왜 인증을 강제로 시도하려 하는지 사용자가 생각해보게 하려는 의도도 있음. 그런 상황은 엔드포인트 침해를 나타낼 수 있기 때문임
    • 아마 여기인 것 같음: https://github.com/NHAS/wag/blob/cdbdbec3393fa86bf6c823117c8...
    • 이 구현의 세부는 모르겠지만, 보통 TOTP 단계까지 갈 수 있는 로그인 정보, 즉 사용자명과 비밀번호를 이미 갖고 있다면 그 사용자는 이미 침해된 상태임
  • Headscale이나 Tailscale과 매우 비슷하게 들림. WireGuard 네트워크를 관리하는 대안이 보이는 건 좋음
    기능이 어디까지 겹치고, 무엇이 추가됐고, 무엇이 다르며, 앞으로도 구현하지 않을 것이 무엇인지 이해할 수 있는 비교 자료가 있는지 궁금함

    • WireGuard를 쓴다는 점에서는 확실히 비슷함
      문서에 직접 비교를 넣지는 않았는데, 현재 내가 가려는 방향은 아님. 이 프로젝트는 내 필요에 맞고 꽤 재미있음
      Wag는 모든 것이 서로 닿고 규칙이 오버레이를 정의하는 Tailscale식 메시보다는, 단단한 경계를 원하는 허브 앤 스포크 구조에 적합함
      wag와 Tailscale 모두 SSO 통합과 사용자 보호를 위한 사실상 2FA를 추가함
      둘 다 등록 방법과 관리를 위한 웹 UI가 있지만, 나는 웹 개발을 좋아하지 않는 1인 개발자라 Tailscale이 훨씬 더 다듬어져 있을 것임
      확실히 구현하지 않을 것은 세션 로그아웃 후 사용자를 리디렉션하기 위한 가로채기나 TLS 프록시 쪽임. 주된 이유는 지금 당장 그걸 eBPF로 하는 게 내겐 조금 벅차고, 동작하게 만들려면 아마 작성해야 할 DNAT/SNAT 구성요소를 쓰고 싶지 않기 때문임
  • IPv4 전용이라니, WireGuard를 선택하는 사이트라면 더 현대적인 구성을 갖추고 자체 서비스형 ULA를 많이 쓸 수도 있을 것 같음

    • 조만간 IPv6 지원을 추가할 계획이고, 사용자의 실제 로컬 네트워크와 충돌할 위험을 줄이기 위해 사람들의 IPv4 주소를 사설 IPv6 공간에 매핑하는 식도 생각 중임
      ULA를 언급할 때 특별히 염두에 둔 점이 있었는지 궁금함
    • 여기서 생각하는 ULA의 장점이 무엇인지 궁금함