วิธีติดตั้ง WireGuard VPN บน Linux VPS ด้วยตัวเอง
เรียนรู้วิธีตั้งค่า WireGuard VPN บน Linux VPS ตั้งแต่การสร้างกุญแจ การเขียนไฟล์ wg0.conf การตั้งค่า IP forwarding และ NAT พร้อมวิธีแก้ไขปัญหา Handshake ล้มเหลวที่พบบ่อย
สิ่งที่คุณกำลังสร้าง
การทำ WireGuard VPN บนเซิร์ฟเวอร์ส่วนตัวใช้การตั้งค่าเพียงประมาณ 40 บรรทัด ได้แก่: คู่กุญแจหนึ่งชุด, ไฟล์อินเทอร์เฟซหนึ่งไฟล์, การตั้งค่า sysctl, กฎ NAT หนึ่งรายการ และการเปิดพอร์ตไฟร์วอลล์ การติดตั้งนั้นทำได้ง่ายมาก ดังนั้นเนื้อหาส่วนใหญ่ในคู่มือนี้จึงครอบคลุมถึงสิ่งที่มักจะเกิดปัญหา, สิทธิ์การเข้าถึงกุญแจ, AllowedIPs, การส่งต่อข้อมูล (forwarding) และ DNS
WireGuard เป็นอุโมงค์ Layer 3 ที่ทำงานในระดับ kernel ซึ่งรวมอยู่ใน mainline ตั้งแต่ Linux 5.6 เป็นต้นมา ดังนั้น Ubuntu 24.04 และ Debian 13 จึงมาพร้อมกับฟีเจอร์นี้โดยไม่ต้องติดตั้งโมดูลภายนอก ไม่มีการเจรจา cipher, ไม่ต้องมี certificate authority และไม่มีขั้นตอนการใช้ชื่อผู้ใช้หรือรหัสผ่าน: peer หนึ่งรายคือ public key บวกกับที่อยู่ IP ที่กุญแจนั้นได้รับอนุญาตให้ใช้งาน แพ็กเก็ตที่ผ่านการตรวจสอบ MAC ไม่สำเร็จจะถูกทิ้งโดยไม่มีการตอบกลับ ดังนั้นพอร์ตจะไม่ตอบสนองต่อการสแกน ข้อเสียคือไม่มีเซิร์ฟเวอร์ยืนยันตัวตน ดังนั้นการเพิกถอนสิทธิ์เข้าถึงจึงต้องทำโดยการลบ peer ออกจากเครื่องโดยตรง
ตรวจสอบระบบเวอร์ชวลไลเซชันก่อน
WireGuard จำเป็นต้องใช้เคอร์เนลที่สามารถโหลดโมดูลได้ ซึ่งบน KVM VPS จะใช้งานได้ทันที แต่สำหรับการทำเวอร์ชวลไลเซชันแบบคอนเทนเนอร์ที่ใช้เคอร์เนลร่วมกับโฮสต์ เช่น OpenVZ หรือ LXC คำสั่งแรกจะล้มเหลวด้วย RTNETLINK answers: Operation not supported และต้องเปลี่ยนไปใช้ wireguard-go ซึ่งเป็นอิมพลีเมนเทชันระดับ userspace แทน ให้ตรวจสอบด้วย sudo modprobe wireguard && echo ok ก่อนเสมอ
การสร้างคีย์โดยไม่ให้ข้อมูลรั่วไหล
การที่ /etc/wireguard/server.key อนุญาตให้ทุกคนอ่านได้นั้นไม่ต่างจากการไม่มี VPN เลย คำสั่ง umask 077 && wg genkey | sudo tee ... ที่ใช้กันทั่วไปนั้นใช้งานได้จริง เว้นแต่ว่า sudoers จะตั้งค่า umask_override ไว้ ซึ่งโดยปกติจะปิดไว้เป็นค่าเริ่มต้น โดย sudo จะนำ umask ของคุณไปรวมกับค่าเริ่มต้น 0022 และจะไม่ลดระดับลง ดังนั้นไฟล์ที่ tee สร้างขึ้นจึงมีโหมด 600 สิ่งที่ทำให้คีย์รั่วไหลคือการรัน pipe นั้นโดยไม่มี umask 077 ภายใต้ umask 022 ตามปกติ หรือบนเครื่องที่ sudoers เปิด umask_override ไว้ การกำหนดโหมดอย่างชัดเจนจะช่วยให้ปัญหานี้หมดไป
sudo apt update && sudo apt install -y wireguard nftables
sudo install -d -m 700 /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server.key > /etc/wireguard/server.pub'
sudo chmod 600 /etc/wireguard/server.keyสร้างคู่คีย์สำหรับไคลเอนต์ด้วยวิธีเดียวกัน wg genpsk จะเพิ่ม pre-shared key ซึ่งเป็นตัวเลือกเสริม โดยระบุไว้หนึ่งบรรทัดในไฟล์คอนฟิกของแต่ละฝั่ง
อินเทอร์เฟซของเซิร์ฟเวอร์: /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <contents of /etc/wireguard/server.key>
[Peer]
PublicKey = <laptop public key>
PresharedKey = <psk, optional>
AllowedIPs = 10.8.0.2/32chmod 600 แก้ไขไฟล์นี้ หากมีคำเตือนตอนเริ่มต้นว่าไฟล์นี้เปิดให้ผู้อื่นเข้าถึงได้ แสดงว่าคุณข้ามขั้นตอนการตั้งค่าสิทธิ์ไป Address คือที่อยู่ของเซิร์ฟเวอร์ภายในอุโมงค์ ซึ่งระบุ subnet mask ของ VPN ทั้งหมด ให้เลือกช่วง IP ที่คุณจะไม่พบในการใช้งานจริงทั่วไป เพราะ 192.168.1.0/24 มักจะซ้ำกับเราเตอร์ตามบ้านประมาณครึ่งหนึ่งที่ไคลเอนต์ของคุณใช้งานอยู่ และจะทำให้อุโมงค์ไม่สามารถรับส่งข้อมูลได้โดยไม่มีการแจ้งเตือนเนื่องจากเกิดการชนกับเส้นทางภายในเครือข่ายท้องถิ่น
AllowedIPs ของ peer ในฝั่ง เซิร์ฟเวอร์ คือ /32 ซึ่งเป็นที่อยู่ภายในอุโมงค์เพียงหนึ่งเดียวที่ไคลเอนต์นั้นเป็นเจ้าของ หากคุณกำหนดให้ peer สองตัวใช้ allowed IP เดียวกัน การเชื่อมต่อจะย้ายไปที่ peer ตัวที่ถูกตั้งค่าล่าสุด และตัวแรกจะหยุดรับส่งข้อมูลโดยไม่มีข้อความแสดงข้อผิดพลาดปรากฏขึ้นที่ใดเลย อย่ากำหนดค่า SaveConfig หรือปล่อยให้ว่างไว้ มิฉะนั้น wg-quick down จะเขียนทับไฟล์นี้ด้วยสถานะการทำงานปัจจุบัน
เปลี่ยนเซิร์ฟเวอร์ให้เป็นเราเตอร์
เซิร์ฟเวอร์ Linux จะทิ้งแพ็กเก็ตที่ไม่ได้ส่งถึงตัวมันเองโดยค่าเริ่มต้น ทั้งการส่งต่อแพ็กเก็ต (forwarding) และการทำ source NAT จึงยังไม่ถูกเปิดใช้งาน
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
| sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forwardการใช้ sysctl -w เพียงอย่างเดียวจะใช้งานได้จนกว่าจะรีบูตเครื่อง หลังจากนั้นจะหยุดทำงานโดยไม่มีการแจ้งเตือน การทำ NAT จำเป็นต้องระบุอินเทอร์เฟซขาออก (egress interface) ซึ่งก็คือ NIC ที่เชื่อมต่อกับอินเทอร์เน็ต ไม่ใช่ wg0 อย่าทึกทักเอาเองว่าเป็น eth0 ให้ตรวจสอบชื่ออินเทอร์เฟซของคุณจาก ip route show default เนื่องจากอิมเมจปัจจุบันมักใช้ชื่ออย่าง enp1s0 หรือ ens3
Firewall: พอร์ตและเส้นทางการส่งต่อ (forward path)
ไฟล์ nftables เพียงไฟล์เดียวครอบคลุมทั้งการกรอง (filter) และ NAT ให้เขียน /etc/nftables.conf ซึ่งจะเป็นการ ล้าง (flush) กฎที่มีอยู่เดิมทั้งหมด ดังนั้นให้ข้ามขั้นตอนนี้หากเซิร์ฟเวอร์ของคุณมีการจัดการด้วย ufw หรือ Docker อยู่แล้ว
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
tcp dport 22 accept
udp dport 51820 accept
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "wg0" oifname "enp1s0" accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" masquerade
}
}นำกฎไปใช้ด้วย sudo systemctl enable --now nftables โดยต้องเปิด SSH session สำรองไว้เสมอ เพราะ policy drop หากพิมพ์กฎ SSH ผิดพลาด คุณจะถูกล็อกออกจากเซิร์ฟเวอร์ทันที โปรดสังเกตว่า forward chain ไม่อนุญาตให้ทำสิ่งใดบ้าง ตั้งแต่ wg0 ถึง wg0 โดยปกติ peer จะเข้าถึงได้เพียงอินเทอร์เน็ตแต่ไม่สามารถเข้าถึงกันเองได้ หากต้องการให้ peer เชื่อมต่อกันเองให้เพิ่ม iifname "wg0" oifname "wg0" accept สำหรับส่วนที่ peer จะเข้าถึงตัวเซิร์ฟเวอร์โดยตรงนั้นไม่ใช่หน้าที่ของ chain นี้ เนื่องจากแพ็กเก็ตที่ส่งถึงตัวเซิร์ฟเวอร์จะผ่าน input hook ไม่ใช่ forward ดังนั้น input chain จึงเป็นตัวตัดสิน ซึ่งตามที่เขียนไว้ในตัวอย่างนี้จะอนุญาตให้ peer เข้าถึงได้เพียง SSH เท่านั้น แม้แต่การ ping ไปยัง 10.8.0.1 ก็ทำไม่ได้ ให้เพิ่ม iifname "wg0" icmp type echo-request accept เข้าไปที่นั่น รวมถึงเพิ่มกฎ iifname "wg0" อีกหนึ่งรายการต่อบริการที่คุณต้องการให้เข้าถึงได้ผ่านอุโมงค์ (tunnel) เท่านั้น ซึ่งมีความสำคัญเมื่อเซิร์ฟเวอร์ของคุณทำหน้าที่เป็น remote development box ที่รัน Claude Code ใน tmux และคุณไม่ต้องการเปิดเผยส่วนนั้นสู่สาธารณะ
สำหรับเซิร์ฟเวอร์ที่ใช้ ufw: ให้ใช้ ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" ใน /etc/default/ufw และเพิ่มกฎ *nat POSTROUTING MASQUERADE ไว้ที่ส่วนบนสุดของ /etc/ufw/before.rules
การเปิดใช้งานภายใต้ systemd
sudo systemctl enable --now wg-quick@wg0
sudo wg showwg-quick จะสร้างอินเทอร์เฟซ เพิ่มที่อยู่ IP และติดตั้งเส้นทางเครือข่ายที่ได้มาจาก AllowedIPs ส่วน enable --now คือส่วนสำคัญที่ขาดไม่ได้ เพราะการรัน wg-quick up wg0 ด้วยตนเองจะหายไปหลังจากรีบูตเครื่อง และการอัปเกรดเคอร์เนลจำเป็นต้องมีการรีบูตเสมอ หาก unit ไม่สามารถกลับมาทำงานได้หลังจากรีบูต ระบบจะเงียบไปจนกว่าจะมีคนพยายามเชื่อมต่อ ดังนั้นการใช้ OnFailure= แบบ drop-in ที่ wg-quick@wg0 โดยชี้ไปยัง เซิร์ฟเวอร์ ntfy ของคุณเอง จึงเป็นวิธีที่ประหยัดที่สุดในการรับการแจ้งเตือนผ่านโทรศัพท์ แทนที่จะรอให้ผู้ใช้งานที่เข้าถึงระบบไม่ได้เป็นคนแจ้งเข้ามา
การตั้งค่าฝั่งไคลเอนต์ และจุดที่ทุกคนมักเข้าใจผิด
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25AllowedIPs ทำหน้าที่สองอย่างพร้อมกัน และการนำสองหน้าที่นี้มาปนกันคือสาเหตุหลักของความสับสนในการใช้งาน WireGuard
ในขาออก มันคือตารางเส้นทาง (routing table) แพ็กเก็ตที่มีปลายทางตรงกับ AllowedIPs ของเพียร์จะถูกเข้ารหัสและส่งไปยังเพียร์นั้น 0.0.0.0/0, ::/0 จะส่งทุกอย่างผ่านอุโมงค์ (full tunnel) โดยให้เซิร์ฟเวอร์เป็นเส้นทางเริ่มต้น ส่วน split tunnel จะเป็นรายการที่แคบกว่า เช่น AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 ที่ส่งเฉพาะทราฟฟิก VPN และเครือข่ายส่วนตัวหนึ่งวงที่อยู่หลังเซิร์ฟเวอร์ ส่วนทราฟฟิกอื่นจะใช้เส้นทางเดิมตามปกติ รายการที่แคบนี้ช่วยให้คุณเก็บเซอร์วิสต่างๆ ไว้ไม่ให้เข้าถึงได้จากอินเทอร์เน็ตสาธารณะ เช่น อินสแตนซ์ Nextcloud ส่วนตัวบน VPS ที่ผูกไว้กับที่อยู่ของอุโมงค์ หรือ VM ในแล็บแบบ nested-virtualisation ที่รันบนเครื่องเดียวกัน ซึ่งจะยังคงเข้าถึงได้เฉพาะเพียร์และไม่ปรากฏให้ผู้อื่นเห็น
ในขาเข้า มันคือรายการควบคุมการเข้าถึง (access-control list) แพ็กเก็ตที่ถอดรหัสแล้วจากเพียร์ซึ่งมีที่อยู่ต้นทางไม่อยู่ใน AllowedIPs ของเพียร์นั้นจะถูกทิ้ง นี่คือเหตุผลที่เซิร์ฟเวอร์ต้องระบุ 10.8.0.2/32 สำหรับแล็ปท็อป เพราะการใส่ค่า 0.0.0.0/0 ไว้ที่นั่นจะทำให้ไคลเอนต์นั้นสามารถปลอมแปลงที่อยู่ใดๆ ในอุโมงค์ได้
PersistentKeepalive มีไว้สำหรับไคลเอนต์ที่อยู่หลัง NAT ซึ่งเราเตอร์จะเปิดการแมป UDP ไว้เฉพาะในขณะที่มีแพ็กเก็ตไหลผ่านเท่านั้น เมื่อหมดเวลา เซิร์ฟเวอร์จะไม่สามารถติดต่อไคลเอนต์ได้อีก PersistentKeepalive = 25 จะช่วยรักษาการแมปนี้ให้คงอยู่ ให้ตั้งค่านี้ที่ฝั่งไคลเอนต์ ไม่ใช่บนเซิร์ฟเวอร์ที่มี IP สาธารณะ
DNS และการรั่วไหลที่ไม่มีใครสังเกตเห็น
หากไม่มี AllowedIPs = 0.0.0.0/0 และไม่มีบรรทัด DNS = ไคลเอนต์จะยังคงใช้ตัวแก้ไข DNS (resolver) ที่ได้รับมาจากเครือข่ายท้องถิ่น ซึ่งก็คือเราเตอร์ของร้านกาแฟที่ 192.168.1.1 เส้นทางดังกล่าวมีความเฉพาะเจาะจงมากกว่าเส้นทางเริ่มต้น (default route) ดังนั้นคำขอ DNS จึงถูกส่งออกไปผ่านลิงก์ท้องถิ่นในรูปแบบข้อความธรรมดา (cleartext) ในขณะที่ข้อมูลอื่นทั้งหมดถูกส่งผ่านอุโมงค์ (tunnel) แม้ว่าทราฟฟิกจะถูกเข้ารหัสไว้ แต่รายการชื่อโดเมนที่เข้าถึงนั้นไม่เป็นความลับ
มีทางเลือกที่ตรงไปตรงมาอยู่ 2 ทาง คือกำหนดให้ DNS ชี้ไปยัง public resolver (เช่น DNS = 9.9.9.9) ซึ่งจะทำให้คำขอวิ่งผ่านอุโมงค์และออกไปจากเซิร์ฟเวอร์ของคุณ แม้ว่าตัว resolver นั้นจะยังคงเห็นคำขอของคุณอยู่ หรืออีกทางหนึ่งคือรัน unbound หรือ dnsmasq โดยผูกไว้กับ 10.8.0.1, ตั้งค่า DNS = 10.8.0.1 และเพิ่ม udp dport 53 iifname "wg0" accept เข้าไปใน input chain จากนั้นตั้งค่าบรรทัดดังกล่าวแล้วไม่ต้องสนใจตัว resolver อีก ซึ่งจะส่งผลให้ไม่มีการแก้ไขชื่อโดเมนใดๆ เลย
บนไคลเอนต์ Linux นั้น wg-quick จะใช้ DNS ผ่านทาง resolvconf หากไม่มีสิ่งนี้ คุณจะได้รับ resolvconf: command not found ให้ติดตั้ง openresolv หรือตั้งค่า PostUp = resolvectl dns %i 10.8.0.1 บนไคลเอนต์ที่ใช้ systemd-resolved
การเพิ่มและลบ peer โดยไม่ทำให้ tunnel หลุด
การรีสตาร์ทอินเทอร์เฟซเพื่อเพิ่มผู้ใช้จะทำให้ทุกคนที่เชื่อมต่ออยู่หลุดออกไป ให้ผนวกบล็อก [Peer] เข้าไปใน wg0.conf จากนั้นจึงโหลดชุด peer ใหม่โดยไม่ต้องปิดการเชื่อมต่อ
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip จะแสดงค่าคอนฟิกโดยไม่รวมคีย์ที่ใช้เฉพาะกับ wg-quick (Address, DNS, PostUp) และ syncconf จะนำการเปลี่ยนแปลงไปใช้ในขณะที่เซสชันปัจจุบันยังคงทำงานอยู่ คำสั่งนี้จะอัปเดตเฉพาะ peer เท่านั้น หากมีการเปลี่ยนแปลง Address ยังคงจำเป็นต้องสั่ง down และ up ใหม่ทั้งหมด ให้เพิกถอนสิทธิ์ด้วย sudo wg set wg0 peer <public key> remove จากนั้นลบบล็อกดังกล่าวออกจากไฟล์ มิฉะนั้นค่าเดิมจะกลับมาอีกครั้งในการโหลดครั้งถัดไป
รูปแบบความล้มเหลวและข้อความที่คุณจะพบ
Handshake ไม่สำเร็จ wg show แสดงรายการ peer โดยไม่มี latest handshake และฝั่ง client จะบันทึก log ดังนี้:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)ไม่มีข้อมูลส่งมาถึง หรือไม่มีการตอบรับ ให้ตรวจสอบตามลำดับดังนี้: พอร์ต UDP 51820 เปิดอยู่บน firewall ของ VPS และ บน network firewall ของผู้ให้บริการ (ซึ่งมักเป็นส่วนควบคุมแยกต่างหากในแผงควบคุมส่วนใหญ่) หรือไม่, ที่อยู่ Endpoint และพอร์ตถูกต้องหรือไม่, และมีการสลับ key กันหรือไม่ key ในบล็อก [Peer] ของ client ต้องเป็น public key ของ server และในทางกลับกัน หากนำ private key หรือ public key ของ client เองมาใส่ จะเกิดอาการนี้ทันที sudo tcpdump -ni any udp port 51820 บน server จะแสดงให้เห็นว่ามี packet ส่งมาถึงหรือไม่ โดยปกติ kernel module จะไม่บันทึก log ใดๆ ข้อความของ WireGuard จะปรากฏใน dmesg ก็ต่อเมื่อคุณเปิดใช้งาน dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) เท่านั้น และเมื่อเปิดใช้งานแล้ว หาก key ไม่ตรงกัน ระบบจะแสดงข้อความว่า invalid-MAC drop
Handshake สำเร็จ แต่ใช้งานอินเทอร์เน็ตไม่ได้ ping 10.8.0.1 ทำงานสำเร็จ แต่ ping 1.1.1.1 กลับ timeout: แสดงว่าขาดการตั้งค่า forwarding หรือ NAT ให้ตรวจสอบว่า sysctl net.ipv4.ip_forward มีค่าเป็น 1 จากนั้นให้สังเกตตัวนับ (counter) ในขณะที่ client ทำการ ping ด้วย sudo nft list ruleset หรือ sudo iptables -t nat -L POSTROUTING -n -v หากตัวนับในกฎ masquerade เป็นศูนย์ แสดงว่าชื่อ interface ขาออก (egress interface) ไม่ถูกต้อง หากตัวนับเพิ่มขึ้นเรื่อยๆ แต่ไม่มีการตอบกลับ แสดงว่าเป็นปัญหาที่นโยบายของ forward chain
ใช้งานอินเทอร์เน็ตได้ แต่แก้ชื่อโดเมน (DNS) ไม่ได้ ping 1.1.1.1 ทำงานสำเร็จ แต่ curl https://example.com ส่งค่ากลับเป็น Could not resolve host แสดงว่าบรรทัด DNS หายไป หรือระบุ DNS resolver ที่ไม่สามารถเข้าถึงได้จากภายใน tunnel
เว็บไซต์ HTTPS บางแห่งค้าง การใช้งาน SSH และ ping ปกติ แต่หน้าเว็บที่มีขนาดใหญ่จะค้าง ปัญหานี้เกิดจาก path MTU: tunnel มี overhead เพิ่มเติม และลิงก์ระหว่างทางบางจุดทิ้ง packet ที่มีขนาดใหญ่เกินไปโดยไม่มีการส่ง ICMP กลับมา ให้ตั้งค่า MTU ในส่วน [Interface] ของ client บนลิงก์ขนาด 1500-byte ทั่วไป wg-quick จะเลือกค่า 1420 (1500 ลบด้วย overhead 80 bytes) อยู่แล้ว ดังนั้นการระบุ 1420 จึงไม่มีผล ให้ลองใช้ 1380 แล้วตามด้วย 1280 หากการลดค่า MTU ช่วยแก้ปัญหาการค้างได้ แต่ความเร็วในการรับส่งข้อมูลยังไม่น่าพอใจ ให้หยุดการเดาตัวเลขและทำตามขั้นตอน การหาค่า path MTU ที่แท้จริงด้วยวิธี bisection และการทำ TCP MSS clamping ซึ่งจะช่วยตัดสาเหตุที่ไม่ได้เกิดจากตัว tunnel ออกไปได้ด้วย
Interface ไม่ยอมเริ่มทำงาน Address already in use หมายความว่ามี process อื่นใช้งานพอร์ต UDP 51820 อยู่ Cannot find device wg0 หลังจากใช้คำสั่ง up ไม่สำเร็จ มักหมายความว่าไฟล์ config ถูกปฏิเสธ ให้ตรวจสอบ journalctl -u wg-quick@wg0 -n 50
การย้ายจาก Streisand หรือ OpenVPN
Streisand ไม่ได้รับการดูแลรักษาแล้วและ repository ถูกเก็บถาวร การรัน VPN บนระบบอัตโนมัติที่ถูกทิ้งร้างเป็นปัญหาด้านความปลอดภัยที่สะสมไปเรื่อยๆ ไม่สามารถทำการอัปเกรดแบบ in-place ได้ และ PKI ของ OpenVPN ก็ไม่สามารถแปลงรูปแบบได้ เนื่องจาก WireGuard ไม่มีใบรับรอง ไม่มี CA และไม่มีวันหมดอายุ ดังนั้นไคลเอนต์แต่ละรายจึงต้องได้รับคู่กุญแจ (key pair) ใหม่ทั้งหมด
ให้ดำเนินการย้ายแบบขนาน โดย WireGuard บน UDP 51820 สามารถทำงานร่วมกับ OpenVPN บน 1194 บนเซิร์ฟเวอร์เดียวกันได้ ให้ติดตั้ง wg0 ขึ้นมาใหม่ ย้ายไคลเอนต์ทีละราย จากนั้นจึงหยุดการทำงานของบริการเดิม รูปแบบการตรวจสอบสิทธิ์ด้วยชื่อผู้ใช้/รหัสผ่านและการเพิกถอนสิทธิ์ของ OpenVPN ไม่สามารถนำมาใช้ร่วมกันได้ หากคุณต้องการระบบจัดการบัญชีหรือบันทึกการตรวจสอบ (audit trail) ให้เพิ่มเลเยอร์ดังกล่าวไว้เหนือ WireGuard อีกชั้นหนึ่ง
การสำรองข้อมูล การอัปเกรด และข้อจำกัดเมื่อขยายระบบ
/etc/wireguard คือ หัวใจสำคัญของเซิร์ฟเวอร์ ให้สำรองข้อมูลไว้ (sudo tar czf wg-backup.tgz -C /etc wireguard, mode 600, เก็บไว้ภายนอกเซิร์ฟเวอร์) แล้วคุณจะสามารถกู้คืนระบบบน VPS เครื่องใหม่ได้ภายในไม่กี่นาที หากทำ private key ของเซิร์ฟเวอร์หาย คุณจะต้องออก config ให้กับ client ใหม่ทั้งหมด เนื่องจาก client มีการทำ public key pinning ไว้ การอัปเกรดทำได้โดยใช้ apt upgrade ตามปกติและรีบูตหากมีการอัปเดต kernel ส่วน wg-quick@wg0 จะกลับมาทำงานเองหากคุณตั้งค่าให้เริ่มอัตโนมัติไว้
สถานะของแต่ละ peer มีขนาดเล็กและการเข้ารหัสทำงานในระดับ kernel ดังนั้นขีดจำกัดจึงอยู่ที่ CPU และ bandwidth ของ VPS ไม่ใช่ที่ตัว config นี้ ให้วัดประสิทธิภาพด้วย iperf3 ผ่านอุโมงค์แทนการเชื่อตัวเลขที่ระบุไว้ สิ่งที่กลายเป็นภาระเมื่อขยายระบบคือการจัดการ (operations) peer แต่ละตัวต้องการ tunnel IP ที่ไม่ซ้ำกัน และการแก้ไขบล็อก [Peer] ด้วยมือจำนวน 60 ชุดมักนำไปสู่ความผิดพลาดที่ทำให้เกิด AllowedIPs ซ้ำซ้อนได้ง่าย ดังนั้นควรสร้าง config จากสคริปต์ เซิร์ฟเวอร์หนึ่งเครื่องคือหนึ่ง UDP endpoint และเป็นจุดที่อาจเกิดความล้มเหลวเพียงจุดเดียว (single point of failure) และ WireGuard ไม่มีระบบ clustering ดังนั้นการทำ redundancy จึงหมายถึงการมีเซิร์ฟเวอร์เครื่องที่สองพร้อมกุญแจของตัวเอง การหมุนเวียนกุญแจ (key rotation) ยังคงต้องทำด้วยมือ ดังนั้นควรจดบันทึกว่าใครถือกุญแจชุดใดและมีวิธีการเพิกถอนอย่างไร เมื่อการจดบันทึกเริ่มเกินกว่าจะจัดการด้วยไฟล์ข้อความ คำตอบทั่วไปคือการใช้ control plane ครอบทับบน data plane ของ kernel เดิม ซึ่ง การรัน NetBird server ด้วยตนเอง จะช่วยจัดการเรื่องการจัดสรรที่อยู่ การกระจาย peer และ setup keys ที่คุณต้องทำด้วยมือ หากการรัน control plane เองเป็นภาระเกินไป Tailscale มีบริการโฮสต์ให้คุณ และ แผน Personal ฟรีของพวกเขารองรับผู้ใช้ 6 คนพร้อมอุปกรณ์ไม่จำกัด และเริ่มต้นที่ tagged resources เช่น เซิร์ฟเวอร์ ได้สูงสุด 50 รายการ ซึ่งเพียงพอสำหรับระบบส่วนใหญ่ที่ไม่ต้องเสียค่าใช้จ่าย หลังจากจุดนั้นไป ค่าใช้จ่ายจะคิดตามจำนวนคนไม่ใช่จำนวนเครื่อง ดังนั้น สิ่งที่ครัวเรือนหรือทีมขนาดเล็กต้องจ่ายจริง จะขึ้นอยู่กับจำนวนผู้ใช้งาน ไม่ใช่จำนวน peer ที่คุณต้องคอยแก้ไขลงใน wg0.conf ในส่วนนั้น การจัดการ split-tunnel AllowedIPs จะกลายเป็น การประกาศ private range ของคุณจาก subnet router ซึ่งประกาศครั้งเดียวจาก VPS เครื่องเดียวและอนุมัติจากศูนย์กลาง แทนที่จะต้องคัดลอกลงในไฟล์ของ client ทุกเครื่อง การตัดสินใจว่าคุ้มค่าหรือไม่ขึ้นอยู่กับว่า hosted control plane เข้าถึงอะไรได้บ้าง และ มันไม่เคยถือกุญแจที่ใช้เข้ารหัสทราฟฟิกของคุณ แม้ว่ามันจะเป็นตัวตัดสินว่า peer ใดจะรู้จักกันก็ตาม
ทั้งหมดนี้ต้องการ Linux box ที่คุณควบคุมได้, public IP, kernel ที่สามารถโหลดโมดูลได้ และ firewall ที่คุณจัดการได้เองทั้งหมดตั้งแต่ต้นจนจบ
FAQ
ทำไมการทำ handshake ของ WireGuard ถึงไม่สำเร็จ?
wg show การระบุ peer โดยไม่มี latest handshake หมายความว่าแพ็กเก็ตไม่ถูกส่งมาถึงหรือถูกปฏิเสธ ให้ตรวจสอบพอร์ต UDP 51820 ทั้งบน firewall ของ VPS และ firewall ของผู้ให้บริการเครือข่าย ยืนยันโฮสต์และพอร์ตของ Endpoint ให้ถูกต้อง จากนั้นตรวจสอบว่ากุญแจไม่ได้สลับกัน โดยบล็อก [Peer] ของฝั่งไคลเอนต์ต้องระบุ public key ของเซิร์ฟเวอร์เท่านั้น sudo tcpdump -ni any udp port 51820 บนเซิร์ฟเวอร์จะแสดงให้เห็นว่ามีแพ็กเก็ตเข้ามาหรือไม่ ส่วน dmesg จะรายงานเฉพาะความล้มเหลวของ handshake หลังจากที่คุณเปิดใช้งาน dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) แล้วเท่านั้น ซึ่งหากกุญแจไม่ตรงกัน ระบบจะแสดงข้อความ invalid-MAC drop
เชื่อมต่อ tunnel ได้แล้วแต่ใช้งานอินเทอร์เน็ตไม่ได้ เกิดจากอะไร?
หาก ping 10.8.0.1 ทำงานได้แต่ ping 1.1.1.1 หมดเวลา (timeout) แสดงว่ามีปัญหาที่การทำ forwarding หรือ NAT ให้ตรวจสอบว่า sysctl net.ipv4.ip_forward มีค่าเป็น 1 และถูกตั้งค่าไว้ใน /etc/sysctl.d/ ไม่ใช่แค่การใช้ sysctl -w ซึ่งจะหายไปเมื่อรีบูตเครื่อง จากนั้นตรวจสอบกฎ masquerade โดยระบุชื่อ interface ขาออกจริงจาก ip route show default, enp1s0 หรือ ens3 (หรือในบางกรณีคือ eth0)
จำเป็นต้องใส่บรรทัด DNS = ในไฟล์ config ของไคลเอนต์หรือไม่?
หากทำ full tunnel โดยไม่มีบรรทัด DNS = ไคลเอนต์จะยังคงใช้ DNS resolver ที่ได้รับจากเครือข่ายท้องถิ่น ซึ่งจะทำให้การสืบค้นข้อมูลถูกส่งออกไปในรูปแบบ cleartext ผ่านเครือข่ายท้องถิ่นในขณะที่ข้อมูลอื่นถูกส่งผ่าน tunnel ให้ตั้งค่า DNS ไปยัง public resolver หรือรัน unbound/dnsmasq ที่ผูกกับ 10.8.0.1 และเปิดพอร์ต udp dport 53 iifname "wg0" ใน input chain
AllowedIPs ทำหน้าที่อะไร?
มันทำหน้าที่สองอย่าง อย่างแรกคือเป็นตารางเส้นทาง (routing table) สำหรับขาออก โดยทราฟฟิกที่ตรงกับ AllowedIPs ของ peer จะถูกเข้ารหัสและส่งไปยัง peer นั้น อย่างที่สองคือเป็นรายการควบคุมการเข้าถึง (access-control list) สำหรับขาเข้า โดยแพ็กเก็ตที่ถอดรหัสแล้วหากมีต้นทางอยู่นอกเหนือ AllowedIPs ของ peer นั้นจะถูกทิ้ง นี่คือเหตุผลที่ฝั่งเซิร์ฟเวอร์ต้องระบุ /32 แยกตามไคลเอนต์ ในขณะที่ฝั่งไคลเอนต์อาจระบุเป็น 0.0.0.0/0
WireGuard สามารถรันบน VPS ใดก็ได้หรือไม่?
บน KVM VPS ระบบจะทำงานร่วมกับ in-kernel module ได้โดยไม่ต้องตั้งค่าเพิ่มเติม แต่บนระบบ container virtualization ที่ใช้ kernel ร่วมกับโฮสต์ เช่น OpenVZ หรือ LXC นั้น modprobe wireguard จะล้มเหลวด้วยข้อผิดพลาด Operation not supported ซึ่งทางแก้คือการใช้ wireguard-go ที่เป็น userspace implementation ให้รันคำสั่ง sudo modprobe wireguard && echo ok ก่อนเริ่มดำเนินการใดๆ