Wireguard 的 keepalive 設定
2025/5/23更新: 更新問題發生的實際原因與解法
問題
在使用 wireguard 時會發現一個問題,當連線長時間靜默時, wireguard 會自動切斷連線,這時就無法連線到對方。 wireguard 有提供一個對應的設定值PersistentKeepalive
,會隔設定的秒數向對方發送心跳包,但如果只在單端設定, wireguard 會因為對方長時間沒有回應,認為對方斷線,從而切斷連線。
但是有時候 server 端自己無法控制,剛好 server 端又沒有加上PersistentKeepalive
的設定,此時就要用一些其他的方法來維持連線了。
斷線實際發生的原因是上游的路由器在檢測到有長時間的 udp 連接後會自動切斷,因為 udp 沒有連接的概念,所以實際上是根據 IP 五元組來定位連接的(protocal: udp, src ip, src port, dst ip, dst port)
解法
解法其實很簡單,定時向對面發送封包即可,可以將以下指令新增到 crontab,記得新增到可以免密碼使用wg-quick
的 user 上,或者新增到 root 的,我是使用* * * * *(每分鐘發送一次 ping,如果斷線則重啟對應的 wireguard 連線)
1 |
|
雖然上面的方法的確可以變更 src port,來規避路由器的連接切斷,但在被切斷到偵測到後重新連接會有一小段掉線時間,既然找到了真正的問題,那麼就可以來解決了。
解決方法是專門針對這條 wireguard 的連接去做隨機 masquerade,讓出去的 port 單獨隨機一次,然後間隔固定時間切斷對應的 conntrack 連線,強迫 kernel 重新 masquerade,但不影響 wireguard process 接收的 port,以 nftable 設定做示範
1 |
|
每隔10分鐘切斷 conntrack,視上游路由器切斷的頻率調整
1 |
|
經過測試,這樣做的斷線時間應該<0.1s,我使用0.1秒的間隔 ping 也只掉一個包