Jan 27, 2021
See the original English blog post here
ในช่วงปีนี้ VPN เหมือนจะถูกหยิบยกมาพูดกันอย่างแพร่หลายเกินกราฟมากในประเทศไทย อาจจะด้วยสภาวะทางการเมืองและกฎหมายที่ชอบปิดหูปิดตาประชาชน แต่ผมก็ยังเห็นว่าคนไทยยังขาดความรู้ความเข้าใจในเรื่อง VPN ส่วนมากเรื่องที่คนไทยคุยกันคือแอพ VPN ฟรี หรือผู้ให้บริการ consumer VPN ต่างๆ แต่ไม่ยักจะมีใครพูดถึง IPSec, OpenVPN หรือ WireGuard กันซักคน ทั้งที่ซอฟต์แวร์โปรโตคอลพวกนี้แหละโดยเฉพาะ OpenVPN คือ backbone ของ consumer VPN service เพราะฉะนั้นวันนี้ผมจะมาบ่นเรื่อง WireGuard ให้ทุกคนฟังกันครับ
ตัวอย่างการตั้งค่าอยู่ที่ท้ายบทความครับ
WireGuard คือโปรโตคอล VPN แบบใหม่ที่เขียนขึ้นมาเพื่อ Linux kernel โดยเฉพาะ (แต่สามารถใช้บนแพลตฟอร์มอื่นได้)
WireGuard เน้นประสิทธิภาพ ความเร็ว ความปลอดภัย และความเล็กของโค้ด WireGuard มีเพียงแค่ 4,000 กว่าบรรทัด (LoC) เมื่อเทียบกับโปรโตคอลคู่แข่งอย่าง OpenVPN ที่ใหญ่มากๆ 70,000++ LoC ที่ยังต้องใช้ไลบรารี่ OpenSSL อีก 500,000 กว่าๆ LoC! โค้ดเบสที่เล็ก ทำให้ WireGuard มี attack surface ที่เล็กกว่า VPN อื่นๆมากๆ และยังมีประโยชน์ทั้งด้านความเร็วของการรับส่งข้อมูลและความสะดวกในการ port ไปใช้บนแพลตฟอร์มที่ไม่ใช่ Linux และที่สำคัญ คือประหยัด system resource ครับ
นอกจากโค้ดที่เล็กมากแล้ว สิ่งที่ทำให้ WireGuard แตกต่างจาก VPN อื่นๆคือความง่าย (simplicity - KISS) และ strong cryptography โดย WireGuard ได้เลือก cryptographic cypher ไว้แล้วหลายตัว แต่ละตัวทำหน้าที่เฉพาะตัว และผู้ใช้ไม่สามารถเลือก cypher ได้เอง นอกจากนี้ การจัดการคีย์เช่นการลบหรือเปลี่ยนคีย์ต่างๆยังทำได้ง่าย เนื่องจาก WireGuard ไม่ใช้ trusted authority หรือเซิร์ฟเวอร์กลางในการรับรอง certificate เหมือน OpenVPN ซึ่งหมายความว่าการตั้งค่าต่างๆ เราสามารถทำได้ในไฟล์เดียว จากโฮสต์ไหนก็ได้
WireGuard ใช้หลักการ Crypto routing และ UDP เป็นรากฐาน ทำให้โปรโตคอลใหม่นี้สามารถทำงานได้อย่างปลอดภัยกว่า ประสิทธิภาพสูงกว่า และเสถียรกว่า traditional VPN อย่าง OpenVPN และ IPSec มากๆ เพราะ WireGuard ใช้ UDP (ผู้ใช้เลือก TCP ไม่ได้) และถูกเขียนมาอย่างฉลาด การเชื่อมต่อผ่าน WireGuard จึงสามารถ survive สภาวะที่ไม่เอื้ออำนวยได้ดีกว่าโปรโตคอลคู่แข่ง
ความไฮเทคนี้ทำให้นาย Theo De Raddt และ Linus Torvalds (เฮ้ดของ OpenBSD และ Linux kernel ตามลำดับ) ถึงกับต้องร้องว้าวแล้วรีบจัดการ port มาลงแพลตฟอร์มของตัวเอง การที่ WireGuard ได้ไปอยู่ใน Linux kernel เป็นการการันตีว่าโปรโตคอลใหม่นี้ดีจริงและยังมีอนาคตที่สดใสอีกด้วย
จากการใช้งานจริง ผมพบว่า WireGuard มีความเถียรและตั้งค่าโฮสต์ใหม่ได้ง่ายกว่า OpenVPN มากๆ และที่สำคัญที่สุดคือเร็วกว่า ไวกว่า หลุดแล้วต่อติดกลับได้อย่างเสถียรมาก และต้องขอบคุณ WireGuard ที่เป็น UDP-only ที่ทำให้ผมสามารถต่อ VPN หลัง Firewall ที่ต่างๆได้สบายๆ เพราะการเชื่อมต่อผ่าน WireGuard หากมองแค่ชั้น UDP แล้ว Firewall จะเห็นเป็น state-less connection
ในปัจจุบัน WireGuard ได้ถูกรวมไว้ใน Linux kernel (>5.7) และ OpenBSD (>6.8) เป็นที่เรียบร้อย และ Commercial VPN provider หลายๆเจ้าก็กำลังทำ WireGuard ไว้เป็นออปชันให้ลูกค้าเลือก หากคุณสนใจเรียนรู้เกี่ยวกับ WireGuard ลองดูวีดีโอนี้บนยูทูป จากผู้พัฒนา WireGuard ครับ
ปัจจุบัน นอกจากแอพ stand-alone สำหรับ macOS, iOS, Android, และ Windows แล้ว ซัพพอร์ตสำหรับ WireGuard บน Linux Desktop เองก็ถือว่าดีขึ้นมากและอยู่ในระหว่างการพัฒนาจริงจัง ทั้งใน systemd-networkd
และ NetworkManager
เราสามารถติดตั้ง WireGuard ได้ง่ายๆบน OS ยอดนิยมต่างๆเช่น macOS, iOS, Android, และ Windows เพียงดาวน์โหลดแอพ WireGuard จาก App Store (ง่ายที่สุด) หรือจากเว็บไซต์ WireGuard การเซ็ทอัพ WireGuard บนแต่ละแพล็ตฟอร์มมีความคล้ายกันมาก โดยผู้ใช้ macOS, iOS, Windows สามารถอ่านการตั้งค่าบน GNU/Linux หรือ OpenBSD เพื่อนำไปประยุกต์ต่อได้
ไม่จำเป็นสำหรับผู้ใช้ที่ใช้ WireGuard ในรูปแบบแอพ
WireGuard จะทำงานได้ก็ต่อเมื่อเราได้ตั้งค่า IP forwarding เป็น 1
หรือ true
ใน kernel setting (sysctl(8)
) โดยคีย์เวิร์ดของการตั้งค่า sysctl
จะแตกต่างกันไปตามแต่ละแพลตฟอร์ม โดยไฟล์ตัวอย่างข้างล่างเป็นการสร้างไฟล์เพื่อตั้งค่า sysctl
แบบ Persistent สำหรับ GNU/Linux (Arch Linux):
# sysctld='/etc/sysctl.d';
# mkdir -p $sysctld;
# ipfwd="$sysctld/50-ip_forwarding";
# touch $ipfwd;
# echo 'net.ipv4.ip_forward=1' >> "$ipfwd";
# echo 'net.ipv6.conf.all.forwarding=1' >> "$ipfwd";
Persistent IP forwarding สำหรับ OpenBSD:
# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf;
# echo 'net.inet6.ip6.forwarding=1' >> /etc/sysctl.conf;
หรือหากคุณต้องการตั้งค่าแบบ one-shot (reboot แล้วเซ็ทติ้งหาย) บน GNU/Linux
# sysctl -w net.ipv4.ip_forward=1;
# sysctl -w net.ipv6.conf.all.forwarding=1;
หรือหากคุณต้องการตั้งค่าแบบ one-shot (reboot แล้วเซ็ทติ้งหาย) บน OpenBSD
# sysctl net.inet.ip.forwarding=1;
# sysctl net.inet6.ip6.forwarding=1;
ผู้ใช้ควรเปิด Firewall หลังเปิดใช้งาน IP forwarding
การใช้งาน WireGuard บน UNIX จำเป็นต้องมี
kernel module (wg(4)
) ซึ่งมีอยู่ใน Linux Kernel (>5.7) หรือ OpenBSD kernel อยู่แล้ว
user-space utilities (wg(8)
, wg-quick(1)
) ซึ่งสามารถติดตั้งได้จากแพ็คเกจ wireguard-tools
ติดตั้ง WireGuard (user-space utilities) ด้วย package manager
# pkg_add wireguard-tools;
# pacman -S wireguard-tools;
# dnf install wireguard-tools;
# apt install wireguard-tools;
ติดตั้ง WireGuard (Linux <5.7 kernel module) ด้วย package manager
# pacman -S wireguard-dkms;
# pacman -S wireguard-lts;
ต้องมี superuser privilege เพื่ออ่าน
/etc/wireguard
คอมมานด์ข้างล่างจะสร้าง key pair foo.pub
และ foo.key
ไว้ใน /etc/wireguard
ด้วย wg(8)
:
# cd /etc/wireguard;
# wg genkey | tee foo.key | wg pubkey > foo.pub;
หากไม่ใช้ pipeline เอาท์พุทของ
wg(8)
จะออกมาที่ stdout
เพื่อเพิ่มความปลอดภัย เราสามารถสร้าง Pre-shared key (psk) สำหรับ WireGuard ด้วย:
# wg genpsk;
WireGuard มาพร้อม bash
สคริปต์ wg-quick(1)
ที่ช่วย bring up อินเตอร์เฟซ wg(4)
สำหรับ WireGuard VPN บน GNU/Linux
wg-quick(1)
จะอ่านการตั้งค่าจาก /etc/wireguard/wgX.conf
(แทน X
ด้วยชื่ออินเตอร์เฟซ เช่น /etc/wireguard/wg0.conf
)
การตั้งค่า WireGuard บน GNU/Linux, macOS, iOS, Android, และ Windows ใช้ภาษาที่เกือบจะเหมือนกันในการตั้งค่า (ชื่อ setting field เหมือนกัน) การตั้งค่า WireGuard จะมี 2 ส่วนหลักๆ คิอส่วนของ [Interface]
สำหรับอินเตอร์เฟซ (ตัวโฮสต์เอง) และ [Peer]
สำหรับ remote hosts โดยเราสามารถเพิ่ม peer ได้ด้วยการเขียน [Peer]
เพิ่มขึ้นมา:
[Interface]
.
.
[Peer]
.
.
[Peer]
.
.
[Peer]
.
.
บน GNU/Linux การตั้งค่าของ server และ client จะคล้่ายๆกัน ต่างกันที่ [Interface]: PostUp
, [Interface]: PostDown
ซึ่งฝั่ง client ไม่จำเป็นต้องมี หากต้องการเชื่อมต่อแบบ point-to-point
เซ็ทติ้งข้างล่างเป็นการเซ็ทอัพไคลเอนท์ 10.8.0.14
ที่จะมี 2 peers 10.8.0.1
และ 10.8.0.2
โดยเน็ตเวิร์คเป็นแบบ 10.8.0.0/24
สำหรับเน็ตเวิร์คนี้ (10.8.0.0/24
) ต้องกำหนด [Interface]: Address
(IP address) ของโฮสต์เราเองด้วย CIDR /24
สำหรับ [Peer]: AllowedIPs
ให้ใส่ IP address ของเน็ตเวิร์ค VPN ของเรา ในกรณีนี้คือ 10.8.0.0/24
และ peer IP address เช่น 10.8.0.2/32
เติม
0.0.0.0/0
และ::1/0
ใน[Peer]: AllowedIPs
หากต้องการให้ peer ไหนเป็น WireGuard default gateway สำหรับทั้ง IPv4 และ v6 (หรือมุดอุโมงค์ไปโผล่ที่ peer นั้นนั่นเอง)เซ็ทติ้งข้างล่างมีการใช้ DNS server ในวง WireGuard เอง และใช้
PreSharedKey
สำหรับ10.8.0.1
-10.8.0.14
[Interface] # Client
PrivateKey = uBxlYH6/fdAy4FfJxquw/Jes+jMntIAxC5Tn65Jwpn0=
Address = 10.8.0.14/24
DNS = 10.8.0.2, 10.8.0.1
PostUp = iptables -t nat -A POSTROUTING -o %i -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o %i -j MASQUERADE
[Peer]
EndPoint = 43.69.233.467:55555
PublicKey = q6n2vXR3NSoy2A6OSBElR95JerCXnJLpdGS8RxuFs1s=
PreSharedKey = JWZkesHbaSYMzjRcSK4j/Q0wunWYSa4LkTSwxEtzJzM=
AllowedIPs = 10.8.0.1/32
#AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
[Peer]
Endpoint = my.domain.com:22134
PublicKey = OmhQUrpLIzc1fRxpBRdpLwV63bAiYRHCbX6nV07nYQw=
AllowedIPs = 10.8.0.2/32
PersistentKeepAlive = 25
เซิร์ฟเวอร์ WireGuard ต่างจากไคลเอนท์ ตรงที่เซิร์ฟเวอร์จำเป็นต้องมี [Interface]: Endpoint
, [Interface]: ListenPort
เพื่อให้ peer หาตัวเซิร์ฟเวอร์เจอ และคำสั่ง iptables
(+ip6tables
) ใน [Interface]: PostUp
, [Interface]: PostDown
เพื่อ route ทราฟฟิค (ทั้ง IPv4, v6) ของ peer:
WireGuard server ในตัวอย่างข้างล่าง ใช้อินเตอร์เฟซชื่อ
eno1
หากคอมพิวเตอร์ของคุณต่อกับอินเตอร์เน็ตได้หลายอินเตอร์เฟซ และต้องการจะใช้ WireGuard บนอินเตอร์เฟซพวกนั้น ให้เพิ่มคำสั่งiptables
และip6tables
สำหรับอินเตอร์เฟซนั้นๆด้วย หาชื่ออินเตอร์เฟซได้ด้วย$ ip a;
หรือ$ ifconfig;
บน GNU/Linux
[Interface] # Server
PrivateKey = APkD7ksO40RWUZDkYU7FwDecqqTS0+rGSbhIHSqSPFk=
Address = 10.8.0.1/24
ListenPort = 55555
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eno1 -j MASQUERADE;\
ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eno1 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eno1 -j MASQUERADE;\
ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eno1 -j MASQUERADE
[Peer]
EndPoint = 45.76.190.176:51543
PublicKey = Z432a5ZyKhcbRvbuH6HFPShmiODjLkL/K+rqX6uqaG0=
PreSharedKey = JWZkesHbaSYMzjRcSK4j/Q0wunWYSa4LkTSwxEtzJzM=
AllowedIPs = 10.8.0.14/32
#AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 60
[Peer]
Endpoint = my.domain.com:22134
PublicKey = OmhQUrpLIzc1fRxpBRdpLwV63bAiYRHCbX6nV07nYQw=
AllowedIPs = 10.8.0.2/32
PersistentKeepAlive = 25
หลังจากตั้งค่าเสร็จเรียบร้อย ในระบบที่มี Systemd เราสามารถ enable (หรือแค่ start) เซอร์วิส wg-quick@wgX.service
ได้ โดย Systemd จะใช้ wg-quick(8)
เพื่อ bring up/down อินเตอร์เฟซ wgX
ตัวอย่างข้างล่างจะ enable และ start เซอร์วิสสำหรับอินเตอร์เฟซ wg0
(ไฟล์ตั้งค่าอยู่ที่ /etc/wireguard/wg0.conf
):
# systemctl enable --now wg-quick@wg0.service;
เราสามารถเช็คการเชื่อมต่อได้ด้วย wg(8)
:
# wg;
หรือ:
# wg show;
หน้าตาของเอาท์พุทก็จะประมาณนี้ครับ (ผมใส่ value มั่วๆนะครับ):
$ sudo wg;
interface: wg0
public key: 4HBWx19Jl5YogSzi+l0akxXBhPVZruwx+zDDv/UEFXI=
private key: (hidden)
listening port: 69420
peer: EJQ2dniqR4J/CnEncl7Mg6pT/co57irBLmG0pVL9XXc=
endpoint: 122.213.43.23:11210
allowed ips: 10.5.0.1/32
latest handshake: 13 seconds ago
transfer: 1.12 MiB received, 10.24 MiB sent
peer: +A+LL2YlR0PMqCIJONl+8kMppJpOvfkAEUCegwYEoUk=
preshared key: (hidden)
endpoint: 12.23.23.21:55569
allowed ips: 10.5.0.2/32
latest handshake: 1 minute, 22 seconds ago
transfer: 281.91 KiB received, 197.54 KiB sent
persistent keepalive: every 25 seconds
สามารถดูคู่มือ wg(8)
ได้หากไม่เข้าใจความหมายของเอาท์พุทครับ
# man 8 wg;
hostname.if(5)
ผู้ใช้ OpenBSD ทุกคนควรอ่านคู่มือ
man
สำหรับhostname.if(5)
และwg(4)
เราสามารถใช้ WireGuard บน OpenBSD ได้ แม้จะไม่ติดตั้ง user-space tools wireguard-tools
ซึ่งมาพร้อม wg(8)
และ wg-quick(8)
เพียงแค่เขียนไฟล์ /etc/hostname.wgX
(แทน X
ด้วยชื่ออินเตอร์เฟซ):
# Interface configuration
wgkey sPXc4K/SXu8oYcEbVentbh7EShxRFR6nccR98GRyX1U=
wgport 22134
inet 10.8.0.2/24
up
# Adding WireGuard peers
!ifconfig wg0 wgpeer Z432a5ZyKhcbRvbuH6HFPShmiODjLkL/K+rqX6uqaG0= wgendpoint 10.10.0.1 55555 wgaip 10.8.0.1/32
!ifconfig wg0 wgpeer Z432a5ZyKhcbRvbuH6HFPShmiODjLkL/K+rqX6uqaG0= wgaip 10.8.0.14/32
หลังจากเขียนไฟล์สำหรับตั้งค่า WireGuard เสร็จ ควรเช็ค pf.conf(5)
เพื่อดูว่า firewall ทำงานกับ WireGuard ได้ไหม ผมเคยเขียนบล็อกเกี่ยวกับการตั้งค่า pf(4)
สำหรับ OpenBSD ไว้แล้วเมื่อปีที่แล้ว โดยทั่วไปเราน่าจะต้องเพิ่มบรรทัดที่หน้าตาคล้ายๆ:
# pf config for WireGuard
pass in on egress inet proto udp from any to any port $wgports
pass out on egress inet from ($wgif:network) nat-to (egress:0)
หลังจากตั้งค่าทุกอย่างเสร็จ ให้ reboot แล้วรออ่าน boot messages เพื่อดูว่าอินเตอร์เฟซ wgX
ของเรา up หรือเปล่า จากนั้นให้ลองดูสถานะการเชื่อมต่อด้วย:
# ifconfig -A;
ที่ผมใช้ ifconfig(8)
แทน wg(8)
ในการเช็คสถานะ เพราะว่าไม่ต้องการติดตั้ง user-space utilities สำหรับ WireGuard wireguard-tools
ครับ
หากไม่มี root provilege แล้ว
ifconfig -A
จะไม่เห็น public keys ของ peers
ต้องอย่าลืมว่าเราสามารถอ่านคู่มือได้ตลอดหากสงสัยว่าไดรเวอร์ wg(4)
ทำงานยังไงบน OpenBSD ครับ:
# man 4 wg;
หากคุณติดตั้ง wireguard-tools
คุณก็สามารพอ่านคู่มือของ user-space utility wg(8)
ได้ด้วย:
# man 8 wg;
ขอให้โชคดีกับ VPN ของตัวเองครับ!