نصب و راهاندازی WireGuard روی VPS لینوکس
آموزش کامل تنظیمات wg0.conf، فعالسازی IP forwarding و رفع خطاهای handshake در WireGuard روی Linux VPS. راهنمای جامع برای مدیریت کلیدها و تنظیمات NAT.
آنچه در حال ساخت آن هستید
راهاندازی یک WireGuard VPN روی سرور شخصی شما، حدود 40 خط تنظیمات است: یک جفت کلید، یک فایل interface، یک تنظیم sysctl، یک قانون NAT و یک پورت باز در firewall. فرآیند نصب بسیار ساده است، بنابراین بیشتر این راهنما به مواردی میپردازد که باعث بروز مشکل میشوند: مجوزهای کلید (key permissions)، AllowedIPs، forwarding و DNS.
WireGuard یک تونل Layer 3 در هسته (kernel) است که از نسخه Linux 5.6 به بعد به صورت اصلی (mainline) در دسترس است؛ بنابراین Ubuntu 24.04 و Debian 13 آن را بدون نیاز به ماژول خارجی ارائه میدهند. در این پروتکل خبری از مذاکره بر سر الگوریتمهای رمزنگاری (cipher negotiation)، مرجع صدور گواهی (certificate authority) یا مرحله ورود با نام کاربری/رمز عبور نیست: یک peer در واقع شامل یک public key به همراه آدرسهای IP است که آن کلید مجاز به استفاده از آنهاست. اگر بستهای در بررسی MAC با شکست مواجه شود، بدون هیچ پاسخی حذف میشود، بنابراین پورت در برابر اسکنها پاسخی نمیدهد. نکته منفی این است که سرور احراز هویت وجود ندارد، بنابراین برای قطع دسترسی، باید peer مربوطه را از روی سیستم حذف کنید.
ابتدا مجازیسازی را بررسی کنید
WireGuard به هستهای (kernel) نیاز دارد که بتوان ماژولی را در آن بارگذاری کرد؛ در VPSهای مبتنی بر KVM، این قابلیت بهصورت پیشفرض فعال است. در مجازیسازی مبتنی بر کانتینر که از هسته میزبان استفاده میکنند — مانند OpenVZ یا LXC — دستور اول با خطای RTNETLINK answers: Operation not supported مواجه میشود و حالت جایگزین، پیادهسازی فضای کاربری (userspace) یعنی wireguard-go است. ابتدا با استفاده از sudo modprobe wireguard && echo ok وضعیت را بررسی کنید.
تولید کلیدها بدون افشای آنها
یک فایل /etc/wireguard/server.key که برای همه کاربران قابل خواندن باشد، دقیقاً مانند نداشتن VPN است. استفاده از دستور معمول umask 077 && wg genkey | sudo tee ... قابل اعتماد نیست، زیرا sudo مقدار umask مخصوص خود را روی فایلی که tee ایجاد میکند، اعمال میکند. حالت دسترسی (mode) را به صورت صریح تنظیم کنید.
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) اضافه میکند که شامل یک خط در هر فایل config است.
رابط کاربری سرور: /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 این هشدار هنگام شروع به کار، به معنای دسترسی عمومی (world accessible) به فایل است و نشان میدهد که شما این مرحله را نادیده گرفتهاید. Address آدرس سرور در داخل تونل است که ماسک (mask) کل زیرشبکه VPN را با خود دارد. محدوده ای را انتخاب کنید که در شبکههای عمومی با آن تداخل نداشته باشد؛ 192.168.1.0/24 با نیمی از روترهای خانگی که کلاینتها پشت آنها هستند تداخل دارد و در این صورت، تونل بدون هیچ خطایی، اولویت خود را به مسیر محلی (local route) واگذار میکند.
بخش AllowedIPs یک Peer در سمت server، یک /32 است؛ یعنی تنها آدرس تونلی که آن کلاینت مالک آن است. اگر به دو Peer آدرس IP مشابه در بخش allowed IP اختصاص دهید، تنظیمات به آخرین مورد تغییر میکند و نفر اول بدون نمایش هیچ خطایی، دیگر ترافیک را دریافت نخواهد کرد. بخش SaveConfig را بدون تنظیم رها کنید، در غیر این صورت wg-quick down این فایل را بر اساس وضعیت جاری (live state) بازنویسی میکند.
تبدیل دستگاه به یک روتر
یک سرور Linux بستههایی را که برای آن ارسال نشدهاند، دور میاندازد (drop میکند). قابلیت 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 نیاز دارد — یعنی کارت شبکهای (NIC) که به اینترنت متصل است، نه wg0. فرض را بر eth0 نگذارید؛ نام رابط خود را از ip route show default بردارید، زیرا ایمیجهای فعلی از نامهایی مانند enp1s0 یا ens3 استفاده میکنند.
Firewall: the port, and the forward path
One nftables file covers filter and NAT. Write /etc/nftables.conf — it flushes the existing ruleset, so skip this on a box already managed by ufw or 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
}
}Apply it with sudo systemctl enable --now nftables, keeping a second SSH session open: policy drop plus a typo in the SSH rule locks you out of your own server. Note what the forward chain does not allow — wg0 to wg0. Peers reach the internet, not each other; add iifname "wg0" oifname "wg0" accept for a peer-to-peer VPN. The same chain governs what a peer may touch on the server itself, which matters when the box doubles as a remote development box running Claude Code in tmux and you would rather not expose that side of it publicly.
On a ufw box: ufw allow 51820/udp, DEFAULT_FORWARD_POLICY="ACCEPT" in /etc/default/ufw, and a *nat POSTROUTING MASQUERADE rule at the top of /etc/ufw/before.rules.
Bring it up under systemd
sudo systemctl enable --now wg-quick@wg0
sudo wg showwg-quick رابط کاربری را ایجاد میکند، آدرسها را اضافه میکند و مسیرهای مشتق شده از AllowedIPs را نصب میکند. enable --now بخش مهم است: یک wg-quick up wg0 که به صورت دستی اجرا شده باشد، پس از اولین reboot از بین میرود و ارتقای kernel نیز مستلزم reboot است.
تنظیمات کلاینت، و موردی که همه در مورد آن اشتباه میکنند
[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 است.
در حالت خروجی (Outbound)، این یک جدول مسیریابی است. بستهای که مقصد آن با AllowedIPs یک Peer مطابقت دارد، رمزنگاری شده و به آن Peer ارسال میشود. 0.0.0.0/0, ::/0 تمام ترافیک را از تونل عبور میدهد — یک تونل کامل، که سرور را به عنوان مسیر پیشفرض (default route) قرار میدهد. یک تونل تقسیمشده (split tunnel) لیست محدودتری است: AllowedIPs = 10.8.0.0/24, 10.20.0.0/16 ترافیک VPN به علاوه یک شبکه خصوصی پشت سرور را حمل میکند، و بقیه موارد مسیر محلی خود را حفظ میکنند. همین لیست محدود است که به شما اجازه میدهد سرویسها را کاملاً از اینترنت عمومی جدا کنید — یک Nextcloud خصوصی روی یک VPS که به آدرس تونل متصل است، یا ماشینهای مجازی آزمایشگاهی nested-virtualisation که روی همان سیستم اجرا میشوند، برای Peerها قابل دسترس و برای بقیه نامرئی میمانند.
در حالت ورودی (Inbound)، این یک لیست کنترل دسترسی (ACL) است. بستهی رمزگشایی شده از یک Peer که آدرس منبع آن در AllowedIPs آن Peer موجود نباشد، حذف (drop) میشود. به همین دلیل است که سرور برای لپتاپ، 10.8.0.2/32 را لیست میکند: وجود یک ورودی با مقدار 0.0.0.0/0 در آنجا، به آن کلاینت اجازه میدهد هر آدرسی را در تونل جعل (spoof) کند.
PersistentKeepalive برای کلاینتهای پشت NAT است، جایی که روتر نگاشت UDP را فقط تا زمانی که بستهها در جریان هستند، باز نگه میدارد. وقتی این جریان قطع شود، سرور دیگر نمیتواند به کلاینت دسترسی داشته باشد. PersistentKeepalive = 25 این نگاشت را باز نگه میدارد — آن را در کلاینت تنظیم کنید، نه در سروری که دارای IP عمومی است.
DNS، و نشتیای که کسی متوجه آن نمیشود
با وجود AllowedIPs = 0.0.0.0/0 و بدون خط DNS =، کلاینت از همان Resolver استفاده میکند که از شبکه محلی دریافت کرده است — یعنی روتر کافه در 192.168.1.1. آن مسیر نسبت به مسیر پیشفرض (default route) اختصاصیتر است؛ بنابراین پرسوجوهای DNS در قالب cleartext از طریق لینک محلی خارج میشوند، در حالی که سایر ترافیکها تونل شدهاند. ترافیک شما خصوصی است، اما لیست نامها (domain names) خیر.
دو گزینه ایمن وجود دارد. DNS را به یک 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 از طریق resolvconf اعمال DNS را انجام میدهد؛ اگر این مورد وجود نداشته باشد، شما resolvconf: command not found را دریافت خواهید کرد. openresolv را نصب کنید، یا در یک کلاینت systemd-resolved، مقدار PostUp = resolvectl dns %i 10.8.0.1 را تنظیم کنید.
اضافه کردن و حذف کردن peerها بدون قطع شدن تونل
راهاندازی مجدد interface برای اضافه کردن یک کاربر، باعث قطع اتصال تمام کاربران متصل میشود. بلوک [Peer] را به wg0.conf اضافه کنید، سپس peer set را در همان محل reload کنید.
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'دستور wg-quick strip تنظیمات را بدون کلیدهای wg-quick-only (شامل Address، DNS، PostUp) چاپ میکند و syncconf تغییرات را در حالی که sessionهای فعال پابرجا هستند، اعمال میکند. این دستور فقط peerها را بهروزرسانی میکند: اگر Address تغییر کند، همچنان نیاز به دستور کامل down/up دارد. برای لغو دسترسی از sudo wg set wg0 peer <public key> remove استفاده کنید، سپس بلوک را از فایل حذف کنید، در غیر این صورت در reload بعدی مجدداً اعمال میشود.
حالتهای شکست، به همراه رشتههایی که مشاهده خواهید کرد
Handshake هرگز تکمیل نمیشود. wg show همنظیر (peer) را بدون latest handshake لیست میکند و در لاگ کلاینت عبارت زیر ثبت میشود:
Handshake for peer 1 (10.0.0.10:51820) did not complete after 5 seconds, retrying (try 2)هیچ دادهای دریافت نمیشود یا هیچ دادهای پذیرفته نمیشود. این موارد را به ترتیب بررسی کنید: آیا پورت UDP 51820 در فایوال VPS و در فایوال شبکه ارائهدهنده (که در اکثر پنلها جداگانه کنترل میشود) باز است؟ آیا آدرس و پورت Endpoint درست است؟ آیا کلیدها جابهجا شدهاند؟ کلید موجود در بلوک [Peer] کلاینت باید کلید public سرور باشد و برعکس؛ چسباندن یک private key یا کلید public خودِ کلاینت، دقیقاً همین مشکل را ایجاد میکند. دستور sudo tcpdump -ni any udp port 51820 در سرور نشان میدهد که آیا بستهها اصلاً دریافت میشوند یا خیر. ماژول هسته (kernel module) به صورت پیشفرض چیزی ثبت نمیکند؛ پیامهای WireGuard در dmesg تنها پس از فعالسازی dynamic debug (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) ظاهر میشوند و با فعال بودن آن، عدم تطابق کلید به صورت invalid-MAC drop نمایش داده میشود.
Handshake برقرار است، اما اینترنت ندارد. ping 10.8.0.1 با موفقیت انجام میشود اما ping 1.1.1.1 با timeout مواجه میشود: forwarding یا NAT تنظیم نشده است. بررسی کنید که آیا sysctl net.ipv4.ip_forward مقدار 1 را میخواند؛ سپس هنگام ping گرفتن کلاینت، با استفاده از sudo nft list ruleset یا sudo iptables -t nat -L POSTROUTING -n -v شمارندهها را زیر نظر بگیرید. صفر بودن تعداد بستهها در قانون masquerade به این معناست که نام رابط خروجی (egress interface) اشتباه است؛ افزایش شمارنده بدون دریافت پاسخ، به سیاست (policy) زنجیره forward اشاره دارد.
اینترنت کار میکند، اما نامها (DNS) کار نمیکنند. ping 1.1.1.1 با موفقیت انجام میشود و curl https://example.com مقدار Could not resolve host را برمیگرداند. خط DNS وجود ندارد، یا نام یک resolver را نشان میدهد که از داخل تونل قابل دسترسی نیست.
برخی سایتهای HTTPS متوقف میشوند. SSH و ping بدون مشکل کار میکنند؛ اما صفحات سنگین متوقف میشوند. این مشکل مربوط به path MTU است: تونل مقداری سربار (overhead) اضافه میکند و برخی لینکهای میانی، بستههای بیش از حد بزرگ را بدون ارسال پیام ICMP، حذف میکنند. مقدار MTU را در کلاینت روی [Interface] کاهش دهید — ابتدا 1420، سپس 1380 و سپس 1280 را امتحان کنید.
رابط (Interface) از اجرا امتناع میکند. Address already in use به این معناست که فرآیند دیگری پورت UDP 51820 را اشغال کرده است. Cannot find device wg0 پس از یک up ناموفق، معمولاً به معنای رد شدن تنظیمات است؛ journalctl -u wg-quick@wg0 -n 50 را بخوانید.
مهاجرت از Streisand یا OpenVPN
پروژه Streisand دیگر پشتیبانی نمیشود و مخزن آن آرشیو شده است. استفاده از یک VPN بر پایه ابزارهای خودکارِ رها شده، یک مشکل امنیتی تدریجی است. امکان ارتقای مستقیم (in-place upgrade) وجود ندارد و ساختار PKI در OpenVPN قابل تبدیل نیست: WireGuard فاقد گواهی (certificate)، CA و تاریخ انقضا است، بنابراین هر کلاینت یک جفت کلید جدید دریافت میکند.
مهاجرت را به صورت موازی انجام دهید — WireGuard روی UDP 51820 میتواند همزمان با OpenVPN روی پورت 1194 در همان سرور اجرا شود. ابتدا wg0 را راهاندازی کنید، کلاینتها را یکییکی منتقل کنید و سپس سرویس قدیمی را متوقف کنید. مدل نام کاربری/رمز عبور و لغو دسترسی (revocation) در OpenVPN به WireGuard منتقل نمیشود؛ اگر به حسابهای کاربری یا قابلیت ثبت وقایع (audit trail) نیاز دارید، باید آن را در لایهای بالاتر از WireGuard پیادهسازی کنید.
Backups, upgrades, and what strains at scale
/etc/wireguard سرور اصلی است. از آن نسخه پشتیبان تهیه کنید (با sudo tar czf wg-backup.tgz -C /etc wireguard، mode 600، و در خارج از سرور ذخیره شود) تا بتوانید در عرض چند دقیقه آن را روی یک VPS جدید بازسازی کنید. اگر کلید خصوصی سرور را از دست بدهید، تمام تنظیمات کلاینتها باید مجدداً صادر شوند، زیرا کلاینتها به کلید عمومی سرور متصل هستند. ارتقا یک apt upgrade معمولی است و برای بهروزرسانی kernel نیاز به reboot دارد؛ همچنین اگر wg-quick@wg0 را فعال کرده باشید، خودش بازمیگردد.
وضعیت هر peer کوچک است و عملیات crypto در kernel اجرا میشود، بنابراین محدودیت اصلی به جای تنظیمات، میزان CPU و پهنای باند VPS شماست؛ به جای اعتماد به اعداد منتشر شده، میزان مصرف را با iperf3 در طول tunnel اندازهگیری کنید. آنچه در مقیاس بالا با چالش مواجه میشود، عملیات مدیریت است. هر peer به یک tunnel IP منحصربهفرد نیاز دارد و ویرایش دستی 60 بلوک [Peer] باعث ورود AllowedIPs تکراری میشود: تنظیمات را با یک script تولید کنید. هر سرور شامل یک UDP endpoint و یک نقطه شکست (point of failure) است و WireGuard قابلیت clustering ندارد: ایجاد افزونگی (redundancy) مستلزم داشتن سرور دوم با کلیدهای اختصاصی خودش است. چرخش کلید (Key rotation) به صورت دستی باقی میماند، بنابراین یادداشت کنید که چه کسی کدام کلید را دارد و چگونه یک کلید را لغو (revoke) کنید.
همه این موارد به یک سیستم Linux تحت کنترل شما نیاز دارد: یک IP عمومی، یک kernel که بتوانید module را در آن بارگذاری کنید، و یک firewall که کنترل کامل آن در اختیار شما باشد.
FAQ
چرا Handshake در WireGuard هرگز تکمیل نمیشود؟
اگر در دستور wg show یک peer بدون latest handshake مشاهده میکنید، یعنی بستهها یا نمیرسند و یا پذیرفته نمیشوند. پورت UDP 51820 را در هر دو بخش فایروال VPS و فایروال جداگانه شبکه ارائهدهنده بررسی کنید. از صحت host و port در Endpoint اطمینان حاصل کنید. سپس بررسی کنید که کلیدها جابهجا نشده باشند؛ بخش [Peer] کلاینت باید شامل کلید public سرور باشد. دستور sudo tcpdump -ni any udp port 51820 در سرور نشان میدهد که آیا بستهای اصلاً دریافت میشود یا خیر؛ دستور dmesg تنها پس از فعالسازی dynamic debug (با دستور echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control) خطاهای handshake را گزارش میدهد و در آن حالت، عدم تطابق کلید به صورت invalid-MAC drop ظاهر میشود.
تونل متصل میشود اما اینترنت ندارم. مشکل از کجاست؟
اگر ping 10.8.0.1 در حال کار باشد اما ping 1.1.1.1 با timeout مواجه شود، مشکل از forwarding یا NAT است. بررسی کنید که sysctl net.ipv4.ip_forward مقدار 1 را بخواند و این مقدار در /etc/sysctl.d/ تنظیم شده باشد، نه اینکه فقط با یک sysctl -w که پس از reboot از کار میافتد، تنظیم شده باشد. سپس نام rule مربوط به masquerade را بررسی کنید؛ این نام باید رابط egress واقعی شما از دستور ip route show default باشد که معمولاً enp1s0 یا ens3 است و به ندرت eth0 میباشد.
آیا به خط DNS = در کانفیگ کلاینت نیاز دارم؟
در حالت full tunnel و بدون خط DNS =، کلاینت از resolver شبکه محلی استفاده میکند؛ در این حالت، پرسوجوها (queries) به صورت cleartext از لینک محلی ارسال میشوند در حالی که سایر ترافیک از تونل عبور میکند. آدرس DNS را روی یک resolver عمومی تنظیم کنید، یا از unbound/dnsmasq متصل به 10.8.0.1 استفاده کنید و پورت udp dport 53 iifname "wg0" را در input chain باز کنید.
دستور AllowedIPs دقیقاً چه چیزی را کنترل میکند؟
این دستور دو وظیفه دارد. در خروجی (Outbound)، این یک routing table است: ترافیکی که با AllowedIPs یک peer مطابقت دارد، رمزنگاری شده و به آن peer ارسال میشود. در ورودی (Inbound)، این یک access-control list است: بستهی رمزگشایی شدهای که منبع آن خارج از AllowedIPs آن peer باشد، drop میشود. به همین دلیل است که سمت سرور برای هر کلاینت یک /32 لیست میکند، در حالی که سمت کلاینت ممکن است 0.0.0.0/0 را لیست کند.
آیا WireGuard روی هر VPS ای اجرا میشود؟
در KVM VPS، این پروتکل با استفاده از in-kernel module و بدون تنظیمات اضافی کار میکند. در مجازیسازی مبتنی بر container که هسته (kernel) میزبان را به اشتراک میگذارد (مانند OpenVZ یا LXC)، دستور modprobe wireguard با خطای Operation not supported مواجه میشود و راه حل جایگزین، استفاده از پیادهسازی userspace یعنی wireguard-go است. قبل از هر کاری، دستور sudo modprobe wireguard && echo ok را اجرا کنید.