SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor

تشغيل WireGuard بواجهة ويب عبر wg-easy وDocker

شغّل WireGuard خلف واجهة wg-easy باستخدام Docker Compose، مع المنافذ وNET_ADMIN وإعدادات sysctl المهمة، وأنشئ العملاء عبر رموز QR لهواتفك بسهولة.

ما الذي ستبنيه

wg-easy هو WireGuard مع واجهة ويب، ويعمل داخل حاوية Docker واحدة. يدير واجهة WireGuard نيابةً عنك، ويضيف واجهة في المتصفح لإنشاء العملاء. يحصل كل عميل تنشئه على ملف إعداد ورمز QR، لذلك ينضم الهاتف إلى VPN بتوجيه الكاميرا نحو الشاشة.

النفق نفسه هو WireGuard عادي. تنقل وحدة kernel الحزم، لذلك يكون معدل النقل مماثلاً لإعداد مكتوب يدوياً. ما تكسبه هو إدارة دورة حياة العملاء: إضافة النظراء وتعطيلهم وحذفهم دون تحرير ملف إعداد عبر SSH. وما تتخلى عنه هو التحكم المباشر في ذلك الملف، وهذا هو موضوع إعداد WireGuard اليدوي على VPS.

تحتاج إلى مضيف Linux يمكنك إدارته، مثل KVM VPS، مع عنوان IP عام أو اسم نطاق يشير إليه، ومعالج x86_64 أو arm64، وDocker Engine مع Compose plugin، ووصول root. عادةً لا تستطيع تقنيات المحاكاة الافتراضية للحاويات التي تشارك kernel المضيف، مثل OpenVZ أو LXC، تحميل وحدة WireGuard، وستفشل الحاوية في تشغيل الواجهة.

نقل الإصدار 15 الإعدادات خارج البيئة

كُتبت معظم الأدلة التي ستجدها لإصدار wg-easy 14، حيث كنت تضبط WG_HOST على عنوان خادمك وPASSWORD_HASH على تجزئة bcrypt لكلمة مرور المسؤول، وكان كلاهما متغيرَي بيئة. الإصدار 15 إعادة كتابة كاملة. توضح ملاحظات الترحيل الرسمية أن v15 لا يستخدم متغيرات البيئة نفسها التي يستخدمها v14، وأن معظم هذه المتغيرات نُقلت إلى لوحة المسؤول في واجهة الويب.

لذلك لم يعد WG_HOST وPASSWORD_HASH ينفذان أي إجراء. إذا نسخت ملف compose قديماً، فستبدأ الحاوية وتتجاهل هذين السطرين، ثم تطلب منك إنشاء حساب مسؤول في المتصفح. هذا ليس خللاً. بل هو مسار الإعداد الجديد.

اعتباراً من أكتوبر 2026، الوسم الرئيسي الذي يجب تثبيته هو 15. ثبّت الإصدار الرئيسي بدلاً من استخدام latest، لأن الترقية الرئيسية تغيّر تنسيق الإعدادات على القرص، ولن تتمكن من التراجع عنها بطريقة سليمة. أدى الانتقال إلى الإصدار 15 إلى استبدال ملف JSON في v14 بقاعدة بيانات SQLite.

ملف compose

أنشئ دليلاً للمكدس، واكتب ملف compose الرسمي فيه. هذا هو الملف المصدر، بعد حذف الأسطر الاختيارية التي كانت مسبوقة بعلامة تعليق.

sudo mkdir -p /etc/docker/containers/wg-easy
sudo curl -o /etc/docker/containers/wg-easy/docker-compose.yml \
  https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml

يبدو المحتوى كما يلي:

volumes:
  etc_wireguard:

services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:15
    container_name: wg-easy
    networks:
      wg:
        ipv4_address: 10.42.42.42
        ipv6_address: fdcc:ad94:bacf:61a3::2a
    volumes:
      - etc_wireguard:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv6.conf.all.forwarding=1
      - net.ipv6.conf.default.forwarding=1

networks:
  wg:
    driver: bridge
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: 10.42.42.0/24
        - subnet: fdcc:ad94:bacf:61a3::/64

يمثل etc_wireguard وحدة تخزين مسماة تحتوي على مفتاح الخادم وكل عميل تنشئه، وتُخزَّن هذه البيانات في قاعدة بيانات SQLite المسماة wg-easy.db. أنشئ نسخة احتياطية من هذه الوحدة، وإلا فستؤدي إعادة البناء إلى حذف جميع نظرائك. إذا كنت تفضّل رؤية هذه الملفات على نظام ملفات المضيف، فاستبدلها بعملية bind mount، واقرأ الفرق بين bind mount ووحدات التخزين المسماة قبل ذلك، لأن الصلاحيات تختلف بينهما.

سبب الحاجة إلى NET_ADMIN وSYS_MODULE وsysctls

لا يُسمح للحاوية افتراضياً بالتعامل مع مكدس الشبكة، وكل سطر من هذه الأسطر يزيل عائقاً محدداً.

يسمح NET_ADMIN للحاوية بإنشاء واجهة wg0، وتعيين عنوان لها، وكتابة المسارات. من دونه تبدأ الحاوية ثم تتوقف أثناء تفعيل الواجهة، لأن ip link add wg0 type wireguard يعيد Operation not permitted.

يتيح SYS_MODULE، مع mount /lib/modules للقراءة فقط، للحاوية تحميل وحدة WireGuard في النواة إذا لم يكن المضيف قد حمّلها مسبقاً. توجد الوحدة في نواة المضيف، وليس داخل image، ولذلك يجب أن يكون دليل المضيف مرئياً. في النوى الحديثة تكون الوحدة مضمّنة عادةً، ويمكنك التحقق من ذلك باستخدام sudo modprobe wireguard && echo ok على المضيف.

يجعل net.ipv4.ip_forward=1 النواة تعيد توجيه الحزم التي لا تكون موجهة إلى الجهاز نفسه. من دونه يتصل العميل، وتنجح المصافحة، ثم تُسقط كل حزمة متجهة إلى الإنترنت، ولذلك تنتهي مهلة ping 1.1.1.1 بينما تبدو VPN متصلة.

يُفاجئ net.ipv4.conf.all.src_valid_mark=1 بعض المستخدمين. يعلّم WireGuard حزم الخرج الخاصة به حتى لا تُوجَّه مجدداً إلى النفق. يرى ترشيح المسار العكسي الصارم حزمةً لا يطابق عنوان مصدرها المسار المتوقع، فيسقطها. يخبر هذا sysctl النواة بقبول الحزم المعلّمة، وهذا ما يمنع النفق الكامل من تعطيل نفسه.

شغِّلها وأنشئ حساب المسؤول

cd /etc/docker/containers/wg-easy
sudo docker compose up -d
sudo docker compose logs -f

استخدم docker compose up وdocker compose down، وليس start وstop. يعيد start تشغيل الحاويات الموجودة مسبقاً فقط، لذلك لا يُطبَّق أي تغيير أجريته على ملف Compose، بما في ذلك إعدادات الشبكة وsysctl، بينما يعيد up إنشاء كل ما تغيّر. إذا أردت إعادة تشغيل المكدس بعد الإقلاع، فإن restart: unless-stopped يتكفّل بذلك، وتشرح آلية إقلاع خدمات Compose ما تضمنه هذه السياسة وما لا تضمنه.

تستمع واجهة الويب على TCP 51821. عند زيارتها أول مرة، تعرض صفحة إعداد تنشئ فيها حساب المسؤول وتؤكد عنوان المضيف الذي سيستخدمه العملاء للوصول إلى الخادم. يظهر عنوان المضيف هذا في سطر Endpoint داخل إعدادات كل عميل، لذلك يجب أن يكون عنوان IP العام أو اسم DNS لخادم VPS. إذا كان العنوان خاطئاً، فسيشير رمز QR الذي تسلّمه إلى الهاتف إلى وجهة يتعذر الوصول إليها، ولن تكتمل المصافحة.

هناك نقطة أخرى بشأن هذا المنفذ: لا يقدّم wg-easy خدمة TLS بنفسه، ولا يرفض HTTP غير المشفّر. عبر http://، تظل الواجهة محمّلة وتعرض شريط تحذير. ما يتغيّر افتراضياً هو ملف تعريف ارتباط الجلسة، إذ يحمل الراية Secure، ولذلك يتجاهله المتصفح عند الوصول إلى الواجهة عبر HTTP غير المشفّر على عنوان عام، ولا يبقى تسجيل الدخول فعالاً. يزيل INSECURE=true هذه الراية ولا يفعل شيئاً آخر. لا تحتاج إلى تغيير أي إعداد في wg-easy عند وضع Reverse Proxy أمامه لإنهاء TLS، حتى عند استخدام شهادة موقّعة ذاتياً.

لا تنشر منفذ الواجهة على الإنترنت

ينشر ملف Compose المنفذ 51821 على كل الواجهات. هذه صفحة تسجيل دخول لخادم يستطيع توجيه حركة الشبكة، لذلك يجب ألا تكون مفتوحة للجميع. يؤدي نشر منفذ في Docker إلى كتابة قواعد في سلسلة DOCKER، التي تُقيَّم قبل ufw، لذلك لا تغلقه قاعدة منع في ufw. من المهم فهم هذا السلوك بحد ذاته، وتشرح أسباب تجاهل منافذ Docker المنشورة لقواعد ufw ذلك بالتفصيل.

الحل البسيط هو ربط الواجهة بعنوان loopback والوصول إليها عبر نفق SSH:

    ports:
      - "51820:51820/udp"
      - "127.0.0.1:51821:51821/tcp"
    environment:
      - INSECURE=true

ثم نفّذ الأمر التالي من حاسوبك المحمول:

ssh -L 51821:127.0.0.1:51821 youruser@your.server.address

افتح http://127.0.0.1:51821 في المتصفح على حاسوبك المحمول. يشفّر SSH حركة الشبكة، ولا يستجيب المنفذ لأي طرف آخر. يزيل INSECURE=true راية Secure من ملف تعريف ارتباط الجلسة فقط، حتى يحتفظ المتصفح بتسجيل دخولك على عنوان HTTP غير المشفّر هذا. وهذا آمن هنا لأن اتصال HTTP غير المشفّر لا يغادر واجهة loopback.

افتح UDP 51820 وتحقق من كلا الجدارين الناريين

يحتاج WireGuard نفسه إلى إمكانية الوصول إلى UDP 51820 من الإنترنت. ينشره Docker، لكن العديد من مزوّدي الخدمة يضعون جداراً نارياً منفصلاً للشبكة أمام VPS، ولا يعرف Docker شيئاً عنه. افتح المنفذ في الموضعين. إذا كنت تدير جدار المضيف الناري باستخدام ufw، فإن قواعد ufw الأساسية لـVPS تمثل المسار الأقصر مقارنة بكتابة قواعد nftables يدوياً.

تحقق من أن الحاوية تستمع فعلياً:

sudo ss -ulnp | grep 51820

يُفترض أن ترى مأخذ UDP في حالة الاستماع. عدم ظهور أي شيء في ذلك السطر يعني أن الحاوية لم ترفع الواجهة، وسيحدد sudo docker compose logs wg-easy السبب.

إنشاء عميل ومسح رمز QR بهاتف

أنشئ عميلاً في واجهة المستخدم، وامنحه اسماً يمكنك تمييزه لاحقاً، مثل اسم الجهاز الذي ينتمي إليه. يخصّص wg-easy عنوان النفق الحر التالي، وينشئ زوج المفاتيح نيابةً عنك. يعرض كل صف من صفوف العملاء رمز QR وملف .conf قابلاً للتنزيل.

ثبّت تطبيق WireGuard الرسمي على الهاتف، واختر إضافة نفق من رمز QR، ثم وجّه الكاميرا إلى الرمز الظاهر على شاشتك. سيظهر النفق بالاسم الذي أدخلته. فعّله، وسيبدأ صف العميل في واجهة المستخدم بعرض عدادات النقل ووقت أحدث مصافحة. عندما يتصل الهاتف بالنفق، يمكنه الوصول إلى الخدمات التي لم تنشرها على الإنترنت، وبذلك يواصل الهاتف رفع الصور إلى خادم صور مستضاف ذاتياً من أي مكان، من دون أن يكون لدى ذلك الخادم منفذ واحد مفتوح للعالم. وتنطبق الطريقة نفسها على الوسائط؛ إذ تصبح مكتبة Jellyfin أُعيد بناؤها كمتجر فيديو من التسعينيات ممتعة للتصفح من غرفة فندق، مع بقائها خاصة كما كانت على شبكة LAN لديك. وتعمل التنبيهات في الاتجاه المعاكس عبر النفق نفسه، إذ يمكن لـخادم ntfy مستضاف ذاتياً إرسال رسالة إلى ذلك الهاتف لحظة فشل مهمة نسخ احتياطي، من دون أن يستجيب أبداً لطلب من الإنترنت العام.

إذا لم يعرض العميل أي مصافحة بعد تفعيله، فهذا يعني أنه لا يصل إلى الخادم إطلاقاً. يشير ذلك إلى UDP 51820، إما في جدار الحماية لدى موفر الخدمة، أو في عنوان endpoint المضمّن في ملف الإعداد. أما العميل الذي يعرض مصافحة، لكن لا يوفّر اتصالاً فعلياً بالإنترنت، فيشير إلى مشكلة في إعادة التوجيه أو DNS.

على جهاز سطح المكتب، نزّل ملف .conf واستورده إلى عميل WireGuard بدلاً من إعادة كتابته يدوياً. يُنشأ المفتاح الخاص في هذا الملف مرة واحدة، ويُعرض مرة واحدة. تعامل مع الملف كما تتعامل مع مفتاح SSH الخاص.

متى تتجاوز واجهة المستخدم

يكون wg-easy الأداة المناسبة ما دام نظراؤك أشخاصاً وهواتف. الواجهة أسرع من تحرير ملفات الإعداد، وإبطال هاتف مفقود لا يتطلب سوى نقرة واحدة.

ستصل إلى حدودها عندما تريد شيئاً لا تمثله الواجهة. يُعد التوجيه من موقع إلى موقع، حيث يغطي AllowedIPs الخاص بأحد النظراء شبكة فرعية بعيدة كاملة بدلاً من عنوان واحد، أول عائق شائع. وتأتي بعده الأنفاق المنقسمة مع قواعد توجيه لكل نظير، أو إعداد ينشئه أداة التزويد لديك. عندئذٍ لا يكون الإعداد المكتوب يدوياً أصعب، بل مختلفاً فقط، ويوضح دليل WireGuard العادي إنشاء النفق نفسه من wg0.conf. وإذا كنت تفضل إيقاف تشغيل طبقة التحكم بالكامل، فاستعرض مقارنة WireGuard مع Tailscale الخيار المُدار. يعتمد كون ذلك تبادلاً عادلاً على ما يمكن لخادم التنسيق الوصول إليه فعلياً، ويستحق نموذج الثقة في Tailscale القراءة قبل منحه إمكانية الوصول إلى شبكتك. تكون التكلفة عادةً السؤال التالي، وتغطي التغطية الفعلية للخطة المجانية في Tailscale ما يصل إلى ستة مستخدمين دون تكلفة، وللاستخدام غير التجاري فقط، لذلك لا تدفع الأسرة شيئاً بينما لا يتأهل فريق شركة صغيرة مهما قل عدد أفراده. بعد ذلك، تحسب الفوترة المستخدمين بدلاً من الأجهزة، وهذا يختلف عن تكلفة VPS التي تدفعها مسبقاً، لذلك تحقّق من تكلفة Tailscale بعد تجاوز الخطة المجانية قبل نقل فريقك. للنفق الكامل الذي أنشأته للتو مكافئ مباشر هناك، إذ يتيح الإعلان عن VPS بوصفه عقدة خروج في Tailscale استخدام المسار نفسه عبر الخادم، مع اعتماد ذلك في وحدة تحكم الإدارة بدلاً من كتابته في إعداد كل عميل. ولحاجز الشبكة الفرعية مكافئ أيضاً، لأن الإعلان عن شبكة خاصة كاملة من VPS يتيح تلك الشبكة لكل جهاز في tailnet دون تحرير AllowedIPs لكل نظير، وهو التحرير الذي دفعك إلى تجاوز الواجهة. وإذا كنت تريد لوحة المعلومات والتوجيه الشبكي التلقائي، لكنك لا تريد خادم تنسيق مملوكاً لجهة أخرى، فإن تشغيل خادم NetBird الخاص بك على VPS يبقي طبقة التحكم على أجهزة تملكها، مقابل إعداد DNS وTLS الذي لم يطلبه منك wg-easy.

إذا كان تركيب Compose المذكور أعلاه هو الجزء غير المألوف، وليس WireGuard، فإن أساسيات Docker Compose على VPS يشرح تنسيق الملف والأوامر اليومية.

FAQ

لماذا يتجاهل wg-easy المتغيرين WG_HOST وPASSWORD_HASH؟

ينتمي هذان المتغيران إلى wg-easy 14. أما الإصدار 15 فأُعيدت كتابته، ونقل المشروع المصدر معظم الإعدادات إلى لوحة المسؤول في واجهة الويب. لا يقرأ container أيّاً من المتغيرين، لذلك يبدأ بصورة طبيعية ثم يطلب منك إنشاء حساب مسؤول عند الزيارة الأولى. اضبط عنوان المضيف الذي سيستخدمه العملاء في صفحة الإعداد تلك.

هل أحتاج إلى SYS_MODULE إذا كانت النواة تحتوي بالفعل على WireGuard؟

لا. يوجد SYS_MODULE وmount /lib/modules حتى يتمكن container من تحميل الوحدة عندما لا يحتويها المضيف. على مضيف ينجح فيه sudo modprobe wireguard مسبقاً، لا تُستخدم هذه capability. يُعد حذفها خطوة معقولة لتعزيز الأمان، بينما يظل NET_ADMIN مطلوباً في كلتا الحالتين.

يتصل العميل، لكن لا يوجد اتصال بالإنترنت. ما المشكلة؟

تعني المصافحة من دون حركة شبكة في الغالب وجود مشكلة في forwarding. تأكد من بقاء net.ipv4.ip_forward=1 وnet.ipv4.conf.all.src_valid_mark=1 في ملف Compose، لأن النسخة المعدّلة يدوياً تفقدهما كثيراً. إذا كان forwarding مفعّلاً، فتحقق من خادم DNS الذي تلقّاه العميل. قد يبدو النفق الذي يرسل كل حركة الشبكة عبر VPN، لكنه يستخدم خادم DNS لم يعد قادراً على الوصول إليه، كأنه اتصال متوقف في المتصفح.

كيف أنشئ نسخة احتياطية من عملائي؟

يوجد كل شيء في named volume etc_wireguard. في الإصدار 15، يعني ذلك قاعدة بيانات SQLite في /etc/wireguard/wg-easy.db داخل container. لا تذكر وثائق v15 أي ميزة للنسخ الاحتياطي أو التصدير، لذلك انسخ volume نفسه احتياطياً: شغّل docker compose down، وانسخ محتويات volume إلى مكان خارج الخادم، ثم أعد تشغيل المكدس باستخدام docker compose up -d. نفّذ ذلك قبل أي ترقية. ينتمي ملف wg0.json الذي تذكره الأدلة الأقدم إلى wg-easy 14. وهو الملف الذي يصدّره زر النسخ الاحتياطي في v14، أما في v15 فاستخدامه الوحيد هو الملف الذي ترفعه في معالج الإعداد عند ترحيل تثبيت قديم.

هل يمكنني تشغيل wg-easy خلف Reverse Proxy؟

نعم. ضع الـproxy أمام منفذ واجهة الويب، وأنهِ TLS هناك، واترك INSECURE غير مضبوط. يقبل container اتصال HTTP غير المشفّر من الـproxy كما هو، ولا تضبط أمثلة Caddy وTraefik الرسمية المتغير INSECURE إطلاقاً. بما أن المتصفح يتصل بالـproxy عبر HTTPS، فأنت تريد أن يحتفظ ملف تعريف ارتباط الجلسة براية Secure، وهي الشيء الوحيد الذي يزيله INSECURE=true. أبقِ UDP 51820 منشوراً مباشرة، لأن حركة VPN تستخدم UDP ولا تمر عبر HTTP proxy.

هل كان هذا المقال مفيدًا؟