wireguard active healthcheck - 1
Netmaker로 Mesh VPN을 구축하고 나서 특정 피어간의 연결이 원활하게 되지 않는 현상을 발견했다.
이상한 점은 트러블 슈팅을 진행하다가 3~4시간이 지나니 연결이 원활하게 되는것이다.
(아마 wireguard에서 본으로 바인딩하는 포트가 막혀있다가 중간에 STUN을 거쳤든 포트를 재할당하다가 연결되는것으로 추측중)
단순히 개발 환경을 구성하고 편의성을 위해 사용하는게 아니라 인프라 구성요소로 작동하기 때문에, 네트워크의 신뢰성을 측정하고 평가하는 방안이 필요하다는 생각이 들었다.
따라서 Peer간의 연결에 대한 상태 지표를 개발하고 이를 모니터링 하기로 결정했다.
Netmaker는 Wireguard를 기반으로 작동하기 때문에, 결국 Wireguard를 대상으로 Metric을 수집 및 개발하면 되겠다는 판단을 했다.
Netmaker에서는 Prometheus 로 각종 Metric을 Export해주는 기능이 내장되어있다.
하지만 유료기 때문에 이를 직접 구현하고자 한다.
export하는 Metric을 확인해보니 Rx, Tx같은 와이어가드에서 자체적으로 기록하는 지표에 더불어 Connected, Uptime, TotalTime, Latency 등 Active Health에 대한 지표를 상세하게 쪼개놨다.
결국 내가 개발하고자 하는 Peer간 연결에 대한 상태 지표를 개발하는 가장 쉬운 방법은,
이미 누군가 개발했을법한 Wireguard Exporter에 Peer에 대한 Ping 결과를 export하는 것이다.
Ping 결과 하나만으로 Connectivity, Uptime 등을 모두 표현할 수 있기 때문이다.
WireGuard: fast, modern, secure VPN tunnel
먼저 WireGuard의 특성에 대해 요약하자면 Connectionless가 가장 대표적인 특징이다. UDP를 기반으로 Peer간의 연결이란 개념이 없다. 전송 할 데이터가 발생하면 상대방이 bind중인 UDP 포트로 패킷을 캡슐링해서 던진다. 이로 인해 불필요한 세션 유지를 위한 Heartbeat가 발생하지 않는다(하지만 이게 필요한 경우가 있음). Peer간의 식별은 IP나 Port가 아닌, Cryptographic 기반의 비대칭 키를 이용한다. (public key와 private key) 따라서 로밍같이 물리적인 이동에 의해 Peer의 아이피가 바뀌는 상황에서도 Seamless하게 터널링이 유지된다.
WireGuard는 Chatty하지 않은 프로토콜인 점이 장점이지만, 네트워크 환경에 의해 주기적으로 연결 유지를 위해 Persistent Keepalive 옵션을 제공한다. NAT나 Firewall에 의해 UDP 포트의 외부 내부 포트 매핑이 유지되지 않는 상황을 방지하기 위해 Persistent Keepalived 로 지정한 초 마다 패킷을 전송해서 포트 매핑을 유지할 수 있다.
WireGuard 프로토콜 설계상 주기적으로 Peer를 대상으로 ping을 전송하는게 일종의 안티패턴으로 작용할 것이지만, 현재 Netmaker를 사용하는 상황에서 꼭 측정해야 하는 모니터링 지표이고, 네트워크 부하도 영향을 끼칠 정도는 아니니 추가해도 좋겠다는 판단을 했다.
GitHub - MindFlavor/prometheus_wireguard_exporter: A Prometheus exporter for WireGuard, written in Rust.
A Prometheus exporter for WireGuard, written in Rust. - MindFlavor/prometheus_wireguard_exporter
github.com
오픈소스 WireGuard exporter는 대략 2개 정도 있었는데 그나마 Star 개수가 많은 레포를 선정해서 기능을 추가하기로 했다. 둘 다 러스트로 작성된 프로젝트라 어쩔 수 없이 러스트를 추가적으로 공부했다.
ping을 전송하는 러스틀 라이브러리는 surge-ping을 선택했다
kolapapa/surge-ping: Asynchronous implementation of ping in rust, based on tokio 1.x (github.com)
GitHub - kolapapa/surge-ping: Asynchronous implementation of ping in rust, based on tokio 1.x
Asynchronous implementation of ping in rust, based on tokio 1.x - kolapapa/surge-ping
github.com
Ping이 timeout나는 경우 4초정도 block이 발생하게 될텐데, peer에 대해 순차적으로 날리는 ping이 모두 timeout나면, prometheus scrap 주기인 10초를 훌쩍 뛰어넘어서 수집에 문제가 생길것이라는 생각이 들었다.
정확도는 조금 떨어지더라도 비동기로 묶어서 HOL을 해소하기로 했다.