wg-easy ב-Docker: WireGuard עם ממשק אינטרנט
הפעילו WireGuard עם wg-easy ו-Docker Compose: אילו יציאות לפתוח, מדוע נדרשת NET_ADMIN, אילו sysctls להגדיר ואיך לצרף טלפונים בקוד QR.
מה אתם בונים
wg-easy הוא WireGuard עם ממשק אינטרנט, שפועל בתוך קונטיינר Docker יחיד. הוא מנהל עבורכם את ממשק WireGuard ומוסיף ממשק משתמש בדפדפן ליצירת לקוחות. כל לקוח שאתם יוצרים מקבל קובץ תצורה וקוד QR, כך שטלפון מצטרף ל-VPN באמצעות הפניית המצלמה אל המסך.
המנהרה עצמה היא WireGuard רגיל. מודול הליבה מעביר את המנות, ולכן קצב העברת הנתונים זהה לזה של הגדרה ידנית. היתרון הוא ניהול מחזור החיים של הלקוחות: הוספה, השבתה ומחיקה של עמיתים בלי לערוך קובץ תצורה דרך SSH. החיסרון הוא ויתור על שליטה ישירה בקובץ התצורה. נושא זה מוסבר בהגדרת WireGuard ידנית ב-VPS.
נדרש לכם VPS מסוג KVM עם כתובת IPv4 ציבורית, Docker Engine עם התוסף Compose והרשאת root. וירטואליזציה של קונטיינרים שחולקת את ליבת המארח, כגון OpenVZ או LXC, אינה יכולה בדרך כלל לטעון את מודול WireGuard. לכן הקונטיינר לא יצליח להפעיל את הממשק.
גרסה 15 העבירה את ההגדרות אל מחוץ לסביבה
רוב המדריכים שתמצאו נכתבו עבור wg-easy 14, שבו מגדירים את WG_HOST לכתובת של השרת ואת PASSWORD_HASH לגיבוב bcrypt של סיסמת מנהל המערכת, ושניהם מוגדרים כמשתני סביבה. גרסה 15 היא גרסה שנכתבה מחדש. בהערות ההעברה הרשמיות מצוין במפורש ש-v15 אינה משתמשת באותם משתני סביבה כמו v14, ושמרבית המשתנים הועברו ללוח הניהול בממשק האינטרנט.
לכן WG_HOST ו-PASSWORD_HASH כבר אינם משפיעים. אם תעתיקו קובץ compose ישן, הקונטיינר יופעל, יתעלם מהשורות האלה, ולאחר מכן יבקש מכם ליצור חשבון מנהל בדפדפן. זו אינה תקלה. זהו תהליך ההגדרה החדש.
נכון ליולי 2026, התג הראשי שיש להצמיד הוא 15. הצמידו את הגרסה הראשית במקום להשתמש ב-latest, משום ששדרוג גרסה ראשית משנה את תבנית התצורה בדיסק ולא ניתן לבצע ממנו חזרה נקייה.
קובץ compose
צרו ספרייה עבור ה-stack וכתבו לתוכה את קובץ ה-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::/64etc_wireguard הוא volume בעל שם שמכיל את מפתח השרת ואת כל הלקוחות שתיצרו. גבו את ה-volume הזה, אחרת בנייה מחדש תמחק את כל ה-peers שלכם. אם אתם מעדיפים לראות את הקבצים האלה במערכת הקבצים של המארח, החליפו אותו ב-bind mount וקראו לפני כן את ההבדל בין bind mounts לבין named volumes, מכיוון שההרשאות מתנהגות באופן שונה.
מדוע נדרשים NET_ADMIN, SYS_MODULE ו-sysctl
כברירת מחדל, למכולה אסור לשנות את מחסנית הרשת, וכל אחת מהשורות האלה מסירה חסימה מסוימת.
NET_ADMIN מאפשר למכולה ליצור את ממשק wg0, להקצות לו כתובת ולכתוב נתיבים. בלעדיו המכולה מופעלת ולאחר מכן נכשלת בעת העלאת הממשק, משום ש-ip link add wg0 type wireguard מחזיר Operation not permitted.
SYS_MODULE, יחד עם העגינה לקריאה בלבד של /lib/modules, מאפשר למכולה לטעון את מודול הליבה של WireGuard אם המארח עדיין לא טען אותו. המודול נמצא בליבת המארח, ולא בתוך התמונה, ולכן יש לחשוף את ספריית המארח. בליבה מודרנית המודול בדרך כלל מובנה, ואפשר לאשר זאת באמצעות 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 על מכל שנוצר בהגדרות שונות משאירה את הרשת במצב לא עקבי. אם ברצונכם להחזיר את המחסנית לפעולה לאחר אתחול, restart: unless-stopped כבר מטפל בכך, ו-התנהגות האתחול של שירותי compose מסבירה מה המדיניות הזו מבטיחה ומה אינה מבטיחה.
ממשק האינטרנט מאזין ב-TCP 51821. בביקור הראשון מוצג דף הגדרה, שבו יוצרים את חשבון הניהול ומאשרים את כתובת המארח שבה הלקוחות ישתמשו כדי להתחבר לשרת. כתובת המארח הזו מופיעה בשורת Endpoint בכל קובץ תצורה של לקוח, ולכן היא חייבת להיות כתובת ה-IP הציבורית או שם ה-DNS של ה-VPS. אם הכתובת שגויה, קוד ה-QR שמוסרים לטלפון מפנה ליעד שאינו נגיש, והלחיצה הידנית (handshake) אינה מסתיימת.
נקודה נוספת לגבי היציאה הזו: wg-easy 15 מסרב ל-HTTP רגיל, אלא אם מגדירים את INSECURE=true. אפשר לגשת אליו באמצעות HTTPS עם תעודה שאינה מהימנה, או לסיים את TLS ב-reverse proxy שמוצב לפניו. גישה אליו באמצעות http:// בהגדרות ברירת המחדל אינה אפשרית.
אין לפרסם את יציאת הממשק לאינטרנט
קובץ ה-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 בטוח לשימוש כאן משום שהדילוג על גבי 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 יציין את הסיבה.
יצירת לקוח וסריקת הקוד בטלפון
בממשק, צרו לקוח והעניקו לו שם שתזהו בהמשך, למשל שם ההתקן שאליו הוא שייך. wg-easy מקצה את כתובת המנהרה הפנויה הבאה ומפיק עבורכם את צמד המפתחות. בכל שורת לקוח זמינים קוד QR וקובץ .conf שניתן להורדה.
התקינו בטלפון את היישום הרשמי של WireGuard, בחרו להוסיף מנהרה מקוד QR וכּוונו את המצלמה אל הקוד המוצג במסך. המנהרה תופיע בשם שהקלדתם. הפעילו אותה, ושורת הלקוח בממשק תתחיל להציג מוני תעבורה ואת מועד לחיצת היד האחרונה.
לקוח שאינו מציג לחיצת יד לאחר הפעלתו אינו מגיע כלל לשרת. הדבר מצביע על בעיה ב-UDP 51820, בחומת האש של הספק או בכתובת נקודת הקצה המוטמעת בתצורה. לקוח שמציג לחיצת יד אך אין לו גישה תקינה לאינטרנט מצביע על בעיה בהעברה או ב-DNS.
במחשב שולחני, הורידו את קובץ .conf וייבאו אותו ללקוח WireGuard במקום להקליד את התצורה מחדש. המפתח הפרטי בקובץ נוצר פעם אחת ומוצג פעם אחת. התייחסו לקובץ כפי שאתם מתייחסים למפתח פרטי של SSH.
מתי לעבור מעבר לממשק
wg-easy הוא הכלי המתאים כל עוד העמיתים שלך הם אנשים ומכשירי טלפון. הממשק מהיר יותר מעריכת קובצי תצורה, וביטול הגישה של טלפון שאבד מתבצע בלחיצה אחת.
תגיע למגבלות שלו כאשר תרצה לבצע פעולה שהממשק אינו יודע לייצג. ניתוב מאתר לאתר, שבו AllowedIPs של עמית מכסה רשת משנה מרוחקת שלמה במקום כתובת יחידה, הוא בדרך כלל המכשול הראשון. מנהור מפוצל עם כללי ניתוב לכל עמית, או תצורה שנוצרה באמצעות כלי ההקצאה שלך, הם השלב הבא. בשלב זה ההגדרה הידנית אינה קשה יותר, אלא פשוט שונה, והמדריך הפשוט ל-WireGuard מציג את אותה מנהרה שנבנתה באמצעות wg0.conf. אם אתה מעדיף להפסיק להפעיל את מישור הבקרה לחלוטין, WireGuard בהשוואה ל-Tailscale מסביר את האפשרות המנוהלת.
אם תחביר ה-compose שלמעלה היה החלק הלא מוכר, ולא החלק הקשור ל-WireGuard, יסודות Docker Compose ב-VPS מסביר את מבנה הקובץ ואת הפקודות היומיומיות.
FAQ
מדוע wg-easy מתעלם מהמשתנים WG_HOST ו-PASSWORD_HASH?
משתנים אלה שייכים ל-wg-easy 14. גרסה 15 נכתבה מחדש, ובפרויקט המקור הועברה כמעט כל התצורה לחלונית הניהול בממשק האינטרנט. הקונטיינר אינו קורא אף אחד מהמשתנים, ולכן הוא מופעל כרגיל ולאחר מכן מבקש ליצור חשבון מנהל מערכת בביקור הראשון. הגדירו את כתובת ה-host הפונה ללקוחות בדף ההגדרה.
האם SYS_MODULE נדרש אם ל-kernel כבר יש WireGuard?
לא. SYS_MODULE וה-mount של /lib/modules קיימים כדי שהקונטיינר יוכל לטעון את המודול כאשר ה-host אינו כולל אותו. ב-host שבו sudo modprobe wireguard כבר מצליח, היכולת אינה בשימוש. הסרתה היא צעד סביר להקשחת האבטחה, ו-NET_ADMIN עדיין נדרש בכל מקרה.
הלקוח מתחבר, אך אין גישה לאינטרנט. מה הבעיה?
לחיצת יד ללא תעבורה פירושה כמעט תמיד בעיית העברה. ודאו ש-net.ipv4.ip_forward=1 ו-net.ipv4.conf.all.src_valid_mark=1 עדיין נמצאים בקובץ ה-compose, מכיוון שהעתקה ידנית של הקובץ משמיטה אותם לעיתים קרובות. אם ההעברה מופעלת, בדקו את שרת ה-DNS שהלקוח קיבל. מנהרה ששולחת את כל התעבורה דרך ה-VPN, אך מפנה לשרת DNS שאליו אינה יכולה להגיע עוד, נראית בדפדפן בדיוק כמו חיבור שאינו פעיל.
כיצד מגבים את הלקוחות?
הכול נמצא ב-volume בעל השם etc_wireguard, בתוך קובץ wg0.json. בממשק יש גם לחצן גיבוי שמייצא את אותם נתונים. העתיקו את הקובץ למיקום מחוץ לשרת לפני כל שדרוג. השחזור מתבצע באמצעות העלאה במהלך שלב ההגדרה בקונטיינר חדש.
האם ניתן להפעיל את wg-easy מאחורי reverse proxy?
כן. הציבו את ה-proxy לפני TCP 51821, סיימו בו את TLS, והגדירו INSECURE=true בקונטיינר כדי שיקבל את קפיצת ה-HTTP הפשוטה מה-proxy. השאירו את UDP 51820 מפורסם ישירות, מכיוון שתעבורת ה-VPN משתמשת ב-UDP ואינה עוברת דרך HTTP proxy.