VPN wireguard

WireGuard เป็นโปรโตคอล VPN รุ่นใหม่ที่ออกแบบมาให้ทำงานได้อย่างรวดเร็ว ปลอดภัย และใช้งานง่าย
การส่งข้อมูลแบบ UDP รวดเร็วกว่า TCP เหมาะสำหรับแอปพลิเคชันที่ต้องการความเร็วและ latency ต่ำ เช่น บนมือถือ หรือ IoT devices
รองรับการ roaming ได้ดี เพราะไม่ต้องเก็บสถานะการเชื่อมต่อ เมื่ออุปกรณ์เปลี่ยนเครือข่ายก็ยังสามารถรักษาการเชื่อมต่อ VPN ไว้ได้

โจทย์:

สร้าง VPN server โดยใช้ WireGuard กำหนดลูกข่ายให้มี 2 ตัวคือ Drone (linux) และ Laptop (linux)
Drone และ laptop สามารถติดต่อกันได้แบบ UDP

For VPN server

Linux in server 10.1.10.85
Install with docker
create docker-compose.yml
services: wireguard: image: lscr.io/linuxserver/wireguard:latest container_name: wireguard cap_add: - NET_ADMIN - SYS_MODULE env_file: .env ports: - $SERVERPORT:51820/udp volumes: - type: bind source: $CONFIG_PATH target: /config sysctls: - net.ipv4.conf.all.src_valid_mark=1 restart: unless-stopped
create .env
PUID=1000 PGID=1000 TZ=Etc/UTC SERVERURL=1.10.148.162 SERVERPORT=51820 PEERS=drone,laptop PEERDNS=auto INTERNAL_SUBNET=10.13.13.0 ALLOWEDIPS=0.0.0.0/0 CONFIG_PATH=/home/hgr-apollo/wireguard/config
cap_add เป็นการกำหนด Linux capabilities ให้กับ container ใน Docker Compose
  • NET_ADMIN: ให้สิทธิ์ในการปรับแต่งการตั้งค่าเครือข่ายของ container เช่น การเพิ่มหรือลบ interface, การกำหนด IP address, การเปลี่ยนแปลง routing table เป็นต้น ซึ่งจำเป็นสำหรับการทำงานของ Wireguard ที่ต้องมีการสร้างและจัดการ interface ของ VPN
  • SYS_MODULE: ให้สิทธิ์ในการโหลดและถอนโหลด kernel module ใน container ซึ่งอาจจำเป็นสำหรับการทำงานของ Wireguard ในบางกรณี
PUID ย่อมาจาก Process User ID คือ ID ของผู้ใช้ที่รันโปรเซสนั้นๆ โดยแต่ละผู้ใช้จะมีค่า PUID เป็นของตัวเอง เช่น root user มักจะมี PUID เป็น 0
PGID ย่อมาจาก Process Group ID คือ ID ของกลุ่มผู้ใช้ที่รันโปรเซสนั้นๆ เหมือนกับ PUID แต่ละกลุ่มผู้ใช้ก็จะมีค่า PGID เฉพาะ
PUID และ PGID มักนำมาใช้ในการกำหนดสิทธิ์การเข้าถึงไฟล์และโฟลเดอร์ต่างๆ ในระบบ Unix/Linux โดยผู้ดูแลระบบสามารถตั้งค่าได้ว่าต้องการให้ผู้ใช้ หรือกลุ่มผู้ใช้ PUID/PGID ใดเท่านั้นที่เข้าถึงไฟล์บางอย่างได้ เพื่อรักษาความปลอดภัยของระบบนั่นเอง
การกำหนดค่า PUID=1000 และ PGID=1000 ที่ไม่ใช่ 0 (root) ช่วยเพิ่มความปลอดภัยให้กับระบบ เพราะถ้ามีช่องโหว่เกิดขึ้นกับ Wireguard daemon มันจะทำงานภายใต้สิทธิ์ของ PUID/PGID ที่กำหนด ซึ่งมีสิทธิ์จำกัดกว่า root ทำให้ลดความเสี่ยงในการโจมตีระบบได้
TZ: กำหนด timezone ของ container
SERVERURL: กำหนด URL หรือ IP address ของ Wireguard server
  • หา ip address ที่แท้จริง
    • curl zx2c4.com/ip
SERVERPORT: กำหนด port ที่ใช้สำหรับ Wireguard server
PEERS: กำหนดจำนวน peer ที่ต้องการสร้าง เป็นตัวเลข หรือ เป็น list เช่น drone,laptop
PEERDNS: กำหนดการใช้ DNS ของ peer
  • auto: ค่าเริ่มต้น จะใช้ DNS ของ peer โดยอัตโนมัติ
  • none: ไม่ใช้ DNS ของ peer
  • กำหนดเป็น IP address ของ DNS server ที่ต้องการใช้งาน เช่น 8.8.8.8, 1.1.1.1 เป็นต้น
INTERNAL_SUBNET: กำหนด subnet ภายในสำหรับ Wireguard network
ALLOWEDIPS: กำหนด IP range ที่อนุญาตให้ peer เข้าถึงได้
  • เป็น 0.0.0.0/0 หมายถึงอนุญาตทุก IP
PERSISTENTKEEPALIVE_PEERS: กำหนดชื่อของ peer ที่ต้องการใช้ persistent keepalive
LOG_CONFS: กำหนดให้มีการ log configuration หรือไม่
sysctls ใน Docker Compose เป็นการกำหนดค่า kernel parameters ของ container
net.ipv4.conf.all.src_valid_mark=1 เป็นการตั้งค่า sysctl สำหรับ IPv4 ซึ่งเกี่ยวข้องกับการทำงานของ Wireguard โดยมีรายละเอียดดังนี้
  • net.ipv4.conf.all.src_valid_mark: เป็นการกำหนดค่าให้กับ kernel parameter ที่ชื่อว่า src_valid_mark สำหรับ interface ทั้งหมดใน container
  • src_valid_mark เป็น flag ที่ใช้ระบุว่า packet ที่ถูกส่งออกจาก interface นั้นควรถูกกำหนด mark หรือไม่
  • การตั้งค่าเป็น 1 หมายถึงการเปิดใช้งาน src_valid_mark ซึ่งจะทำให้ packet ที่ถูกส่งออกจาก interface ถูกกำหนด mark ตามที่ต้องการ
ในบริบทของ Wireguard การตั้งค่า src_valid_mark=1 มักถูกใช้เพื่อให้ Wireguard สามารถทำงานร่วมกับ iptables ในการกำหนด mark ให้กับ packet ที่ถูกส่งออกจาก Wireguard interface ได้อย่างถูกต้อง ซึ่งจำเป็นสำหรับการทำงานบางอย่าง
Start VPN server
docker compose up -d
Check status
docker exec -it wireguard wg show
Show QR code connection for peer drone
docker exec -it wireguard /app/show-peer i11
Show text connection for peer laptop
docker exec -it wireguard cat /config/peer_m1max/peer_m1max.conf
[Interface] Address = 10.13.13.3 PrivateKey = CNgLthXcQmQ513rqleAv512ntbKpVRbyUwB1nLiAclA= ListenPort = 51820 DNS = 10.13.13.1 [Peer] PublicKey = qfDqh+G7DcffTEv0QT6xwebKMD+ELAKNCNbZlFN4XnY= PresharedKey = IiG2RfqZlrHvea5TKyUVU7+9oCE5dKEAVUV9+hzBTgY= Endpoint = 1.10.148.162:51820 AllowedIPs = 0.0.0.0/0
Use name instead ip address
Edit /etc/hosts file
10.13.13.2 drone 10.13.13.3 laptop

For Client

Linux

Assume it's a laptop
install
sudo apt install wireguard
Create config file
sudo nano /etc/wireguard/peer.conf
Copy “Result of peer_laptop” and paste
Ctrl + O → Enter → Ctrl + X
Setup peer3 config
sudo wg-quick up peer
[#] ip link add peer3 type wireguard
[#] wg setconf peer3 /dev/fd/63
[#] ip -4 address add 10.13.13.4 dev peer3
[#] ip link set mtu 1420 up dev peer3
[#] resolvconf -a peer3 -m 0 -x
[#] wg set peer3 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev peer3 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0
[#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
[#] nft -f /dev/fd/63
Check Status
sudo wg show
interface: peer
public key: NTMIAsF+vT1pdKQJzz1YIhhk+rnbNOHvTxOTAYRLmiA=
private key: (hidden)
listening port: 51820
fwmark: 0xca6c
peer: qfDqh+G7DcffTEv0QT6xwebKMD+ELAKNCNbZlFN4XnY=
preshared key: (hidden)
endpoint: 1.10.148.162:51820
allowed ips: 0.0.0.0/0
latest handshake: 33 seconds ago
transfer: 3.48 KiB received, 24.85 KiB sentStop vpn connection
test ping vpn server
ping 10.13.13.1
PING 10.13.13.1 (10.13.13.1) 56(84) bytes of data.
64 bytes from 10.13.13.1: icmp_seq=1 ttl=64 time=12.3 ms
64 bytes from 10.13.13.1: icmp_seq=2 ttl=64 time=12.5 ms
64 bytes from 10.13.13.1: icmp_seq=3 ttl=64 time=12.1 ms
Stop VPN
sudo ip link del dev peer

iOS

Assume it's a drone
Open WireGuard → Button + → Create from QR code → type name → Swipe to connect VPN
Test with iSH to ping the laptop
ping 10.13.13.3
Result:

Mac

Open wireguard → Button + → Add Empty Tunnel… → paste “/config/peer[1-5]/peer[1-5].conf”
test
ping 10.13.13.1
PING 10.13.13.1 (10.13.13.1) 56(84) bytes of data.
64 bytes from 10.13.13.1: icmp_seq=1 ttl=64 time=12.3 ms
64 bytes from 10.13.13.1: icmp_seq=2 ttl=64 time=12.5 ms
64 bytes from 10.13.13.1: icmp_seq=3 ttl=64 time=12.1 ms
Built with Potion.so