2020/03/05

WireGuard NAT2NAT連線失敗的經驗分享

WireGuard的連線設定說明

前言

和過去的VPN系統相較, WireGuard並沒有OpenVPN Push Router/user password的設定
因此設定上比較簡單, 但困難度也在這裡, 並像過去route add 的方式新增route tables
老派的IT人員在操作上很容易GG陣亡

設定

wireguard需要透過指令產生private key和Public key.
sudo -i 切成root
wg genkey | tee /etc/wireguard/server-privatekey | wg pubkey > /etc/wireguard/server-publickey

以下為圖例
< Server > ----------------------------------  < Client >​  

  wg genkey                                     wg genkey 

S_Private key                                  C_Private key  

S_Public key                                   C_Public key  

產生後需要手動建立Server的conf , 名稱預設為wg0 /wg1 ...etc.
Client則需在Server端建好或是複製彼此的Public key交換訊息

以下為圖例
< Server > ----------------------------------  < Client >​  

 wg0.conf                                     我是客戶端.conf                                                   

S_Private key                                 C_Private key   

C_Public key                                  S_Public key  

請參考以下我的用戶範例

PC或手機的用戶端檔案 River.conf
[Interface]   #這是建立一個interface, 在mac或是windows下會產生個tun0與server端的wg0做連線  
PrivateKey = CCCCCCCCCCC= #這是C_Private key, wg gen建好複製在此  
Address = 10.10.100.100/32 #要與wg0連線的ip地址, 比較麻煩的是每個用戶需要一個專用ip, 沒法設0自己dhcp抓  

[Peer]  #連線到wg0的peer, 如果你有多用戶, 開個新的peer建, peer不要共用, 因為ip不同     
PublicKey = SSSSSSSSSSS=  #這是S_Public key, 需要從Server wg gen好後複製過來   
AllowedIPs = 10.10.100.2/32, 192.168.0.0/16 #這邊是路由與ip block的封禁區段, 需要另外說明      
Endpoint = xxx.xxx.xxx.xxx:5987 #中華電信的public ip , 或vps服務器遠端的ip      

Server端設定範例 wg0.conf.

[Interface] 
Address = 10.10.100.2/24 #Server wg0 ip  
ListenPort = 5987 #Server暴露的port  
PrivateKey = SSSSSSSSSSS= #Server的Private Key  
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o **br0** -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o **br0** -j MASQUERADE

# 如果是NAT的環境需要將ip封包轉發,你會需要將wg0的封包轉發到你的網卡上**br0**是我的網卡
# 你可能需要檢查一下你的網卡的名稱, 每人不太一樣 eno1, ens4n0, eth0....etc
# 另這行設定完後回去bash命令下, 設定sysctl, 需要將net.ipv4.ip_forward=1 
# sudo vim /etc/sysctl.conf
# 將底下這個設定值修改正確即可! (本來值為 0 ,將它改為 1 即可)
# net.ipv4.ip_forward = 1
# 最後重啟網路
# sudo /etc/init.d/networking restart

# S2S 這是Site to Site的設定   
[Peer]
PublicKey = XXXXXXXXXXXXXX= #對口server public key  
AllowedIPs = 10.10.100.1/32 #對口的wg0的ip地址   
Endpoint = xxx.xxx.xxx.xxx:5987 #對口Public暴露的ip+port   
PersistentKeepalive = 15 #需要保持長連接所以需要PersistentKeepalive的參數  

# 如下範例 ,每個用戶需要將對應的Client端產生的 public key 複製一份給WireGuard Server
# 且重要的, IP不可以衝突, 不然wg會打架連不上去

# User1
[Peer]
PublicKey = QQQQQQQQQQQQQQ=
AllowedIPs = 10.10.100.3/32

# User2
[Peer]
PublicKey = WWWWWWWWWWW=
AllowedIPs = 10.10.100.4/32

AllowedIPs

# 說的比較簡單點這個設定就是路由

Server端和Client端都有AllowedIPs
注意這裡, 需要特別設定這個參數不然會route loop你的Server, 然後就再也連不上你的主機了.
我的Client設定是
AllowedIPs = 10.10.100.2/32, 192.168.0.0/16
預設我可以ping到10.10.100.2, 可以透過公司的路由訪問192.168.2.1/192.168.1.1等兩個區域的網段(以我的例子是台北與雲林)

我的"出口" IP會是我原本的, 不會透過wg0的主機IP
比方說我的手機是211.x.xx.90, wg0是出口是135.xxx.xxx.194, 那我去curl http://ifconfig.me   時會是211.xx.xx.90

那如果此時設定0.0.0.0/0
就會讓所有封包透過的wg0轉發, 此時curl http://ifconfig.me 就會135.xxx.xxx.194

Server端沒事不要設定0.0.0.0/0

除非你很清楚知道你在幹嘛?! @@

因為default 的路由設定0.0.0.0的封包轉發會透過原有的eth0轉包
你同時設置兩個0.0.0.0的時候會造成routing loop
或者你要route del default 在eth0上的路由
但還是一樣, 除非你很清楚知道你route del default路由在幹嘛, 不然不要亂刪

路由的問題待解

我的情境是兩個台北雲林的路由器下方設置Port暴露
然後兩邊WG互聯Site2Site, 或精確一點說NAT2NAT

就我嘗試的情況下, wg0沒辦法在有其他路由路徑的情況下
可以同時訪問外部, 也能同時訪問對面跨區域的服務網段
在NAT下, 同個網段, 
同時有192.168.2.1當路由, 
也同時有wireguard的 server eth0 (192.168.2.xxx) 在NAT下當路由S2S

我嘗試了搜尋兩張網卡的路由設定怎樣做(dual wan, 或是雙網卡)
網上有很多Site2Site的WireGuard設定 
但這些多為透過多台的wg0來組local networking
我一直沒有成功,  可能留待高手來解決

以下是我的試錯經驗:
在Server端, 如果要site2site的
route -n 確認一下路由路徑
千萬不要在Server端的wg0.conf AllowedIPs這段設置, 
應該用route add來新增
我的做法是
修改到default路由, 讓wg0取代default
所有的192.168.1.1流量走wg0
0.0.0.0/0流量走eth0

但用戶這裡我實際測試情況是
(192.168.2.30)需要指定靜態路由來穿透WireGuard的site2site
Windows和Mac沒有IT技能的用戶無法這樣手動添加
一定得登入WireGuard才會有Site2Site的功能, 不太合乎我的區網內的同事用戶情境 

而這台wg就爆炸了, 
因為脫離了上層路由器的gateway設定, 刪除了default路由後, 路由器認不到了
ssh失效就成了個磚頭
我有在回去本機添加192.168.2.1的gateway routing table, 但仍然無法訪問
一定要登入WireGuard or 在自己主機上添加靜態路由

可能的想法我嘗試如下
在Server上面設置RIP動態路由, 目前測試大部分服務是可以通
但ssh因為動態路由關係, 設置後沒多久就會斷線, 靜置沒有任何操作的話, 兩三秒就不能用了
因此最後的解方(?)
應該是找台路由器又能設置Wireguard來連線, 不是透過底下的服務器來NAT2NAT
所以....需要找能負荷多用戶的的OpenWRT或是pfSense, 效能不能太差的硬體.


這有一些坑




如果你用手機網路或是發現連線很慢.... 撇除水管寬度,或是中國網路長城等封包隔離 那你需要來設定MTU
MTU必須依照網路卡與ISP提供商的數值往下扣
MTU值太小會影響頻寬, 但當wg0的MTU與ISP廠商提供的不mapping時
就會造成封包大量遺失

TL;DR懶人包

MTU分片最小值為1280
如果你懶得算, 頻寬對你來說不是很重要,能連又穩定就好
那分片的MTU = 1280
就這樣設定吧...

Example. 
 GCP提供mtu 1460
 wg0 server端不設定mtu自動啟動後會剩下1380
 Client端中華電信 1440, 因此高於1380, 於是封包被分片, 多出來的就drop
 訪問Youtube就會出問題   


計算方式
  • 20-byte IPv4 header or 40 byte IPv6 header
  • 8-byte UDP header
  • 4-byte type
  • 4-byte key index
  • 8-byte nonce
  • N-wbyte encrypted data #256bit加密算法/8 = 32bytes
  • 16-byte authentication tag
 20+8+4+4+8+32+16= 92 
 wg0 1380 -92 = 1288  
 因此MTU值非常低, 1280設定在Client端的conf上面  
 
[Interface]
PrivateKey = MMMMMMMMMMMMMM=
Address = 172.16.10.5/24
DNS = 1.1.1.1, 8.8.8.8
MTU = 1280


連線YT等影片網站就能知道是不是有斷片封包

/* 註記, 每個人的網卡設定值也不同, 如果不同ISP廠商遠程辦公, 還會受到家用AP/路由器的MTU影響 */