استضف VPN من نوع WireGuard على VPS الخاص بك
أعدّ WireGuard على خادم VPS خاص بك يعمل بلينكس: توليد المفاتيح، وwg0.conf، وتمرير IP، وNAT، ودلالات AllowedIPs، وDNS، وإخفاقات المصافحة المؤلمة.
ما الذي تبنيه
شبكة VPN تعمل بـ WireGuard على خادم تملكه أنت هي تقريبًا أربعون سطرًا من الإعدادات: زوج مفاتيح واحد، وملف واجهة واحد، وإعداد sysctl واحد، وقاعدة NAT واحدة، وفتحة واحدة في جدار الحماية. التثبيت بسيط جدًا، لذلك يتناول معظم هذا الدليل ما يتعطل: أذونات المفاتيح، وAllowedIPs، والتمرير (forwarding)، وDNS.
WireGuard نفق (tunnel) من الطبقة الثالثة (Layer 3) يعمل داخل النواة (kernel)، ومدمج في الفرع الرئيسي منذ Linux 5.6، لذلك يوفّره Ubuntu 24.04 وDebian 13 دون أي وحدة (module) خارجية. لا تفاوض على التشفير، ولا جهة تصدر الشهادات، ولا خطوة اسم مستخدم/كلمة مرور: النظير (peer) هو ببساطة مفتاح عام مع عناوين IP التي يحق لذلك المفتاح استخدامها. أي حزمة تفشل في التحقق من MAC تُرفض دون أي رد، لذلك لا يستجيب المنفذ لعمليات المسح (scans). والوجه الآخر لذلك: لا يوجد خادم مصادقة، فإلغاء وصول شخص ما يعني ببساطة حذف نظيره من الجهاز.
تحقّق من نوع الافتراضية أولًا
يحتاج WireGuard إلى نواة يمكنك تحميل وحدة فيها، وعلى VPS يعمل بتقنية KVM يعمل الأمر مباشرة دون أي إعداد إضافي. أما على افتراضية الحاويات التي تشارك نواة المضيف — مثل 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 ... غير موثوق، لأن 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) اختياريًا، سطر واحد في كل إعداد.
واجهة الخادم: /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/32طبّق عليه chmod 600؛ فإن ظهر تحذير عند الإقلاع بأن الملف متاح للجميع، فهذا يعني أنك تجاوزت هذه الخطوة. Address هو عنوان الخادم داخل النفق، ويحمل قناع (mask) الشبكة الفرعية الخاصة بكامل الـ VPN. اختر نطاقًا لن تصادفه في العالم الحقيقي — فالنطاق 192.168.1.0/24 يتصادم مع نصف أجهزة التوجيه المنزلية التي يجلس خلفها عملاؤك، فيخسر النفق حينها بصمت أمام المسار المحلي.
AllowedIPs الخاصة بنظير ما على جانب الخادم هو /32، عنوان النفق الوحيد الذي يملكه ذلك العميل. أعطِ نظيرين العنوان المسموح نفسه، وسينتقل إلى من ضُبط إعداده أخيرًا، فيتوقف الأول عن استقبال أي حركة بيانات دون طباعة أي خطأ في أي مكان. اترك SaveConfig دون ضبط، وإلا فإن wg-quick down يعيد كتابة هذا الملف من الحالة الحيّة الفعلية.
حوّل الجهاز إلى جهاز توجيه
الخادم الذي يعمل بلينكس يُسقط الحزم غير الموجَّهة إليه. التمرير و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.
جدار الحماية: المنفذ، ومسار التمرير (forward)
ملف واحد من nftables يغطي الفلترة وNAT معًا. اكتب /etc/nftables.conf — وهو يُفرِغ مجموعة القواعد الحالية بالكامل، لذلك تجاوز هذه الخطوة على جهاز تديره بالفعل أداة 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 ثانية مفتوحة: فـpolicy drop مع خطأ إملائي في قاعدة SSH يحبسك خارج خادمك أنت نفسه. لاحظ ما لا تسمح به سلسلة forward — الانتقال من wg0 إلى wg0. النظراء يصلون إلى الإنترنت، لا إلى بعضهم بعضًا؛ أضف iifname "wg0" oifname "wg0" accept إن أردت VPN من نوع نظير إلى نظير (peer-to-peer). السلسلة نفسها تحكم ما يستطيع أي نظير الوصول إليه على الخادم ذاته، وهذا مهم حين يكون الجهاز في الوقت نفسه جهاز تطوير عن بُعد يشغّل 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 ينشئ الواجهة، ويضيف العناوين، ويثبّت المسارات المشتقة من AllowedIPs. الجزء المهم هو enable --now: فتشغيل wg-quick up wg0 يدويًا يختفي بعد إعادة التشغيل التالية، وترقيات النواة تعني إعادة تشغيل.
إعداد العميل، والخيار الذي يخطئ فيه الجميع
[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 المختبرية في الافتراضية المتداخلة العاملة على الجهاز نفسه، إذ تبقى تلك الخدمات قابلة للوصول من النظراء وغير مرئية لأي طرف آخر.
في الاتجاه الوارد هو قائمة تحكم بالوصول (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 =، يحتفظ العميل بالمحلِّل (resolver) الذي تعلّمه من الشبكة المحلية — جهاز توجيه المقهى مثلًا عند 192.168.1.1. وبما أن ذلك المسار أكثر تحديدًا من المسار الافتراضي، تخرج استعلامات DNS عبر الرابط المحلي كنص صريح غير مشفّر بينما يمر كل شيء آخر عبر النفق. حركة البيانات نفسها خاصة؛ أما قائمة الأسماء فليست كذلك.
هناك خياران صريحان. وجّه DNS إلى محلِّل عام (DNS = 9.9.9.9) فتسلك الاستعلامات النفق وتخرج من خادمك، رغم أن ذلك المحلِّل يظل يراها. أو شغّل unbound أو dnsmasq مرتبطًا بـ 10.8.0.1، واضبط DNS = 10.8.0.1، وأضف udp dport 53 iifname "wg0" accept إلى سلسلة input — لكن إن ضبطت هذا السطر ونسيت تشغيل المحلِّل، فلن يُحل أي اسم إطلاقًا.
على عملاء لينكس يطبّق wg-quick قيمة DNS عبر resolvconf؛ فإن كان غائبًا تحصل على resolvconf: command not found. ثبّت openresolv، أو اضبط PostUp = resolvectl dns %i 10.8.0.1 على عميل يستخدم systemd-resolved.
إضافة النظراء وحذفهم دون قطع النفق
إعادة تشغيل الواجهة لإضافة مستخدم تطرد كل المتصلين حاليًا. أضف كتلة [Peer] إلى wg0.conf، ثم أعد تحميل مجموعة النظراء في مكانها دون توقف.
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'يطبع wg-quick strip الإعداد دون المفاتيح الخاصة بـ wg-quick وحده (Address، DNS، PostUp)، ويطبّق syncconf الفرق بينما تبقى الجلسات الحيّة صامدة. هو يحدّث النظراء فقط: أي تغيير في Address ما زال يتطلب دورة down/up كاملة. ألغِ نظيرًا بالأمر sudo wg set wg0 peer <public key> remove، ثم احذف كتلته من الملف وإلا عاد في إعادة التحميل التالية.
أنماط الفشل، مع الرسائل التي ستراها
المصافحة (handshake) لا تكتمل أبدًا. يُدرج wg show النظير دون أي 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] الخاصة بالعميل يجب أن يكون المفتاح العام الخاص بالخادم، والعكس صحيح — فلصق مفتاح خاص، أو المفتاح العام الخاص بالعميل نفسه، يؤدي إلى هذا العرَض بالضبط. يُظهر 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، وعندها يظهر عدم تطابق المفاتيح على هيئة رفض بسبب MAC غير صالح.
المصافحة تعمل، لكن لا إنترنت. ping 10.8.0.1 ينجح لكن ping 1.1.1.1 تنتهي مهلته: التمرير أو NAT مفقود. تحقّق من أن sysctl net.ipv4.ip_forward يعرض 1، ثم راقب العدّادات أثناء إرسال العميل لطلبات ping، باستخدام sudo nft list ruleset أو sudo iptables -t nat -L POSTROUTING -n -v. صفر حزم على قاعدة masquerade يعني أن اسم واجهة الخروج فيها خاطئ؛ وعدّاد يرتفع دون أي ردود يشير إلى سياسة سلسلة forward.
الإنترنت يعمل، لكن الأسماء لا تُحل. ping 1.1.1.1 ينجح وcurl https://example.com يعيد Could not resolve host. سطر DNS مفقود، أو أنه يسمّي محلِّلًا لا يمكن الوصول إليه من داخل النفق.
بعض مواقع HTTPS تتجمد. SSH وping يعملان جيدًا؛ لكن الصفحات الكبيرة تتوقف. هذا هو MTU الخاص بالمسار: النفق يضيف عبئًا إضافيًا (overhead)، وبعض الوصلات في المنتصف ترفض الحزم الزائدة الحجم دون أن تعود أي رسالة ICMP. خفّض MTU في [Interface] الخاصة بالعميل — جرّب 1420، ثم 1380، ثم 1280.
الواجهة ترفض البدء. Address already in use تعني أن عملية أخرى تحجز المنفذ UDP 51820. أما Cannot find device wg0 بعد فشل up فتعني غالبًا أن الإعداد رُفض؛ اقرأ journalctl -u wg-quick@wg0 -n 50.
الانتقال من Streisand أو OpenVPN
Streisand لم يعد يحظى بأي صيانة ومستودعه مؤرشف، وتشغيل VPN فوق أتمتة مهجورة مشكلة أمنية تتفاقم ببطء. لا توجد ترقية في مكانها، وبنية OpenVPN التحتية للمفاتيح العامة (PKI) لا تُنقَل كما هي: WireGuard لا شهادات فيه، ولا CA، ولا انتهاء صلاحية، فكل عميل يحصل على زوج مفاتيح جديد تمامًا.
انتقل بالتوازي — WireGuard على المنفذ UDP 51820 يتعايش مع OpenVPN على المنفذ 1194 على الجهاز نفسه. أقم wg0، وانقل العملاء واحدًا تلو الآخر، ثم أوقف الخدمة القديمة. نموذج اسم المستخدم/كلمة المرور والإلغاء الخاص بـ OpenVPN لا ينتقل معه؛ فإن احتجت إلى حسابات أو سجل تدقيق، أضِف تلك الطبقة فوق WireGuard.
النسخ الاحتياطي، والترقيات، وما يُجهِد عند التوسّع
/etc/wireguard هو الخادم بعينه. انسخه احتياطيًا (sudo tar czf wg-backup.tgz -C /etc wireguard، بصلاحيات 600، محفوظًا بعيدًا عن الجهاز) وستتمكن من إعادة بنائه على VPS جديد في دقائق. افقد المفتاح الخاص للخادم وسيتوجب إعادة إصدار كل إعدادات العملاء، لأن العملاء يثبّتون المفتاح العام للخادم في إعداداتهم. الترقيات هي apt upgrade عادي بالإضافة إلى إعادة تشغيل عند تحديثات النواة، وتعود wg-quick@wg0 من تلقاء نفسها إن كنت قد فعّلتها.
حالة كل نظير صغيرة والتشفير يعمل داخل النواة، لذا فإن السقف الحقيقي هو حصة الـ CPU وعرض النطاق الترددي لخادمك، لا أي شيء في هذا الإعداد — قِسه بأداة iperf3 عبر النفق بدلًا من الوثوق برقم منشور في مكان ما. أما ما يُجهِد فعلًا عند التوسّع فهو التشغيل اليومي. كل نظير يحتاج إلى عنوان IP فريد داخل النفق، وتحرير ستين كتلة [Peer] يدويًا هو بالضبط كيف تتسلل قيم AllowedIPs المكررة: ولِّد الإعدادات من سكربت بدلًا من ذلك. خادم واحد يعني نقطة نهاية UDP واحدة ونقطة فشل واحدة، وWireGuard لا يدعم التجميع العنقودي (clustering): التكرار (redundancy) يعني خادمًا ثانيًا بمفاتيحه الخاصة. دوران المفاتيح (key rotation) يبقى يدويًا، لذا سجّل من يملك أي مفتاح وكيف تُلغيه.
كل هذا يحتاج إلى جهاز لينكس تتحكم فيه أنت بنفسك — عنوان IP عام، ونواة يمكنك تحميل وحدة فيها، وجدار حماية تملك زمامه من طرفه إلى طرفه.
FAQ
لماذا لا تكتمل مصافحة WireGuard أبدًا؟
إدراج wg show لنظير دون أي latest handshake يعني أن الحزم إما لا تصل أو لا تُقبل. تحقّق من المنفذ UDP 51820 على جدار حماية الـ VPS وعلى جدار حماية الشبكة المنفصل لدى مزوّد الخدمة معًا، وتأكد من مضيف ومنفذ Endpoint، ثم تحقق من أن المفاتيح غير متبادَلة خطأً — يجب أن تحمل كتلة [Peer] الخاصة بالعميل المفتاح العام للخادم. يُظهر sudo tcpdump -ni any udp port 51820 على الخادم ما إذا كانت الحزم تصل أصلًا؛ ولا يبلّغ dmesg عن إخفاقات مصافحة WireGuard إلا بعد تفعيل التصحيح الديناميكي (echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control)، وعندها يظهر عدم تطابق المفاتيح كرفض بسبب MAC غير صالح.
النفق يتصل لكن لا يوجد لدي إنترنت. ما الناقص؟
نجاح ping 10.8.0.1 بينما تنتهي مهلة ping 1.1.1.1 يشير إلى التمرير أو NAT. تأكد من أن sysctl net.ipv4.ip_forward يعرض 1 وأنه مضبوط في /etc/sysctl.d/، لا بمجرد sysctl -w الذي يزول أثره عند إعادة التشغيل. ثم تحقق من أن قاعدة masquerade تسمّي واجهة الخروج الحقيقية لديك من ip route show default — غالبًا enp1s0 أو ens3، ونادرًا eth0.
هل أحتاج إلى سطر DNS = في إعداد العميل لدي؟
مع نفق كامل ومن دون سطر DNS =، يحتفظ العميل بالمحلِّل الذي تعلّمه من الشبكة المحلية، وتخرج تلك الاستعلامات كنص صريح عبر الرابط المحلي بينما يمر كل شيء آخر عبر النفق. وجّه DNS إلى محلِّل عام، أو شغّل unbound/dnsmasq مرتبطًا بـ 10.8.0.1 وافتح udp dport 53 iifname "wg0" في سلسلة input.
ماذا يتحكم AllowedIPs فعليًا؟
يؤدي وظيفتين. في الاتجاه الصادر هو جدول توجيه: حركة البيانات المطابقة لـ AllowedIPs الخاصة بنظير ما تُشفَّر وتُرسل إلى ذلك النظير. وفي الاتجاه الوارد هو قائمة تحكم بالوصول: أي حزمة مفكوكة التشفير مصدرها خارج AllowedIPs الخاصة بذلك النظير تُرفض. لهذا يدرج جانب الخادم /32 لكل عميل بينما قد يدرج جانب العميل 0.0.0.0/0.
هل يعمل WireGuard على أي VPS؟
على VPS بتقنية KVM يعمل مع الوحدة المدمجة في النواة ودون أي إعداد إضافي. أما على افتراضية الحاويات التي تشارك نواة المضيف، مثل OpenVZ أو LXC، فإن modprobe wireguard يفشل بالرسالة Operation not supported، والبديل هو تنفيذ wireguard-go في مساحة المستخدم. نفّذ sudo modprobe wireguard && echo ok قبل أي شيء آخر.