في الجزء الأول بنينا الخزانة، وفي الجزء الثاني قسمنا الشبكة إلى مناطق وكتبنا من يكلم من، والآن عندنا بيت جاهز لكنه فارغ: لا ملفات، ولا ويكي، ولا دخول موحد، ولا مراقبة، ولا خدمة يصل إليها عميل. وفي هذا الجزء الأخير نضع الخدمات في أماكنها، ونقرر كيف يصل إليها العملاء دون أن نفتح باب البيت، ثم نبني ما يجعل البيئة تشبه الإنتاج فعلاً: المراقبة، والنسخ الاحتياطي، والتحديثات، والتوثيق، وتجربة الكارثة قبل أن تحدث.
المواضيع في السلسلة:
- الهوم لاب بمواصفات الإنتاج (1): التصميم والعتاد: الأهداف، والقرار حول خدمات العملاء، والتصميم المرجعي، والعتاد والكابلات وال UPS.
- الهوم لاب بمواصفات الإنتاج (2): الشبكة والجدار الناري: خطة العناوين، وشبكات VLAN، ومصفوفة المناطق، وOPNsense، وWireGuard.
- الهوم لاب بمواصفات الإنتاج (3): الخدمات ونشرها (الموضوع الحالي).
وكل خدمة في هذا المقال لها دليل تثبيت مستقل في الموقع، لذلك لن نثبت هنا شيئاً شرحناه، وإنما نقرر أين يعمل وكيف يتصل بالبقية، وسوف نناقش في هذا المقال ما يلي:
- ما الذي يعمل أين: الأجهزة الافتراضية VM وLXC Containers وسيرفرات Docker.
- الخدمات الداخلية: الهوية والملفات والويكي، وال Reverse Proxy بشهادات TLS داخلية.
- خدمات العملاء: ال VPS أمام البيت مع نفق WireGuard، أو الخدمة كلها على ال VPS، مع تجربة عملية يصل فيها طلب من الإنترنت إلى البيت.
- المراقبة والتنبيه والسجلات المركزية.
- النسخ الاحتياطي بقاعدة 3-2-1 مع نسخة خارج البيت.
- التحديثات ونوافذ الصيانة، والتوثيق وأدلة التشغيل Runbooks، وقائمة تجربة الكارثة.
ما الذي يعمل أين؟
على سيرفر Proxmox ثلاثة أماكن ممكنة لأي خدمة: جهاز افتراضي كامل VM بنواته الخاصة، أو LXC Container يشارك نواة السيرفر، أو Container داخل جهاز افتراضي فيه Docker. والحل البسيط أن تضع كل خدمة في LXC خاص بها لأنه أخف، أو أن تثبت Docker على سيرفر Proxmox نفسه، وكلاهما يعمل في اليوم الأول، ولكن انظر إلى ما يقوله توثيق Proxmox VE عن LXC Containers: تشغيل Containers التطبيقات من Docker Images مباشرة بصيغة OCI ما زال معاينة تقنية Technology Preview، ولمن يحتاج أعلى عزل والقدرة على النقل الحي Live Migration فإن تشغيل ال Containers داخل جهاز افتراضي ما زال الممارسة الموصى بها، والتوثيق نفسه يذكر أن تشغيل Docker داخل LXC غير مميز يحتاج خيار keyctl ويدخلك في مقايضة مع systemd-networkd. لذلك القاعدة في هذا البيت كما يلي:
- جهاز افتراضي VM لكل مجموعة خدمات مترابطة تعمل في Docker، مثل التطبيقات الداخلية في جهاز، والمراقبة في جهاز آخر، لأن تحديث مجموعة أو استعادتها من Snapshot لا يمس غيرها، وكما شرحنا في تثبيت Docker على Ubuntu.
- جهاز افتراضي VM لما له نظام خاص، مثل Home Assistant OS، أو لما تريد أن تكسره وتعيده في شبكة معزولة.
- LXC لخدمات Linux الخفيفة التي لا تحتاج Docker، مثل خادم السجلات.
- لا شيء على سيرفر Proxmox نفسه، فالسيرفر يشغل الأجهزة الافتراضية فقط، حتى يبقى تحديثه سهلاً.
والصورة التالية تبين التوزيع في هذا البيت:

وأول جهاز افتراضي تنشئه في Proxmox شرحناه خطوة بخطوة في إنشاء أول VM في Proxmox بعنوان IP ثابت، ورقم VLAN لكل جهاز يكتب في إعداد واجهته الشبكية كما ذكرنا في الجزء الثاني.
الخدمات الداخلية للعائلة والفريق
هذه هي الخدمات التي تجعل البيت يخدم العائلة والفريق، وكلها على شبكة السيرفرات، ولا يصل إليها أحد من الإنترنت إلا عبر WireGuard:
| الحاجة | الخدمة | أين | الدليل |
|---|---|---|---|
| الهوية والدخول الموحد SSO | authentik | VM 101 | تثبيت authentik وحماية أي تطبيق ب Forward Auth |
| الملفات والتقويم | Nextcloud أو Seafile | VM 101، والبيانات على ال NAS | Nextcloud وSeafile |
| الويكي وأدلة التشغيل | BookStack | VM 101 | BookStack لتوثيق الفريق |
| لوحة واحدة لكل الخدمات | Homepage | VM 101 | لوحة Homepage |
| المراقبة والتنبيه | Uptime Kuma وPrometheus وGrafana وntfy | VM 102 | أدناه، وntfy |
| السجلات | Loki | LXC 201 | أدناه |
| النسخ الاحتياطي | Proxmox Backup Server وrestic | جهاز مستقل أو ال NAS | أدناه |
والترتيب مهم: ابدأ ب authentik، وملف ترخيصه يجعل الكود الأساسي بترخيص MIT، وما في مجلد authentik/enterprise/ بترخيص منفصل، وآخر إصدار عند كتابة المقال 2026.8.3، والسبب أن كل خدمة تضيفها بعده تأخذ الدخول الموحد من اليوم الأول، فلا يكون عندك عشرة باسوردات لكل فرد في العائلة، وعندما يترك أحد الفريق تعطل حسابه في مكان واحد.
ال Reverse Proxy وشهادات TLS داخل البيت
كل الخدمات الداخلية تمر عبر Reverse Proxy واحد في VM 101، مثل Caddy أو Traefik أو Nginx Proxy Manager، وما يحتاجه التطبيق خلفه من رؤوس وعناوين شرحناه في تطبيقك خلف Reverse Proxy. والسؤال الذي يأتي هنا: كيف تحصل على شهادة TLS لخدمة لا يصل إليها الإنترنت؟ فالتحقق عبر HTTP (HTTP Challenge) يحتاج أن يصل Let's Encrypt إلى سيرفرك على المنفذ 80، وهذا غير ممكن هنا. وهناك طريقتان، وكلتاهما تفترض أنك تفهم الشهادات الرقمية:
- Let's Encrypt مع التحقق من ملكية النطاق عبر ال DNS (DNS Challenge): بحسب صفحة أنواع التحقق تضع أداة ACME سجل TXT في
_acme-challengeتحت نطاقك، وهذه الطريقة تعمل لسيرفر لا يصل إليه الإنترنت، وهي الوحيدة التي تعطيك شهادة Wildcard مثل*.home.example.com. والصفحة نفسها تنبه إلى أن وضع مفاتيح API الكاملة لمزود DNS على سيرفرك خطر، وتنصح بمفاتيح محدودة الصلاحية. ونقاط القوة: كل الأجهزة تثق بالشهادة دون أي إعداد. ونقاط الضعف: أسماء خدماتك الداخلية تظهر في سجلات الشفافية العامة Certificate Transparency إلا إذا استخدمت Wildcard، وتحتاج نطاقاً حقيقياً ومزود DNS له API. - سلطة شهادات داخلية Internal CA: مثل step-ca من Smallstep بترخيص Apache-2.0، وآخر إصدار عند كتابة المقال v0.30.2، أو السلطة المدمجة في Caddy. ونقاط القوة: لا شيء يخرج من البيت، وتصدر شهادات لأي اسم داخلي مثل
pve.home.arpa. ونقاط الضعف: يجب أن تثبت شهادة السلطة الرئيسية Root CA على كل حاسوب وهاتف في البيت، وهذا يصعب مع أجهزة الضيوف والأجهزة الذكية.
والتوصية: Let's Encrypt بالتحقق عبر DNS وشهادة Wildcard لاسم فرعي مخصص للبيت، مع DNS المنقسم من الجزء الثاني، لأن الاسم نفسه يعمل من الداخل ومن الخارج بنفس الشهادة، والسلطة الداخلية لما لا يحتاج اسماً عاماً أبداً، مثل لوحات الإدارة.
خدمات العملاء: ال VPS أمام البيت أو الخدمة كلها عليه
في الجزء الأول وضعنا الأرقام: نسبة توفر البيت، وسرعة الرفع، وCGNAT، وخطر الباب المفتوح، وكانت التوصية أن خدمة العميل لا تكون خلف منفذ مفتوح في البيت. والصورة التالية تبين الطريقتين اللتين تبقيان:

- A: هجين Hybrid. ال VPS هو الواجهة العامة، وعليه Reverse Proxy وشهادة Let's Encrypt عادية، ويمرر الطلبات عبر نفق WireGuard يبدأ من البيت إلى السيرفر في البيت. ويناسب خدمة بياناتها كبيرة في البيت، ويقبل عملاؤها أن تتوقف عندما يتوقف البيت. والطريقة شرحناها كواحدة من طرق النشر في تشغيل خادمك من إنترنت المنزل، وهنا نبنيها كاملة في التجربة العملية أدناه.
- B: الخدمة كلها على ال VPS. التطبيق وقاعدة بياناته على ال VPS، والبيت يسحب نسخة احتياطية مشفرة منه كل ليلة، فيصبح البيت هو النسخة خارج الموقع لل VPS، وال VPS لا يعرف شيئاً عن البيت. ويناسب أي خدمة يدفع لها عميل، وتحتاج تأمين ال VPS من أول دخول واختيار VPS مناسب.
وقد يتساءل البعض: لماذا لا أستخدم Cloudflare Tunnel وأرتاح من ال VPS؟ والإجابة أنه خيار صحيح أيضاً، وقد شرحناه مع بدائله في خدمات Cloudflare وبدائلها وما هو النفق Tunnel، والفرق أن حركة عملائك ومفاتيح TLS تمر عندها، بينما في الطريقة A كل المفاتيح عندك.
تجربة عملية: طلب من الإنترنت يصل إلى البيت عبر نفق
الآن نبني الطريقة A بأصغر شكل ممكن، ونتحقق من ثلاثة أشياء: أن الطلب من الإنترنت يصل إلى سيرفر في البيت ليس له أي منفذ مفتوح، وأن ال VPS لو اخترق لا يرى من البيت إلا خدمة واحدة، وماذا يرى العميل عندما يتوقف البيت. والصورة التالية تبين التجربة:

نستخدم نفس ال Docker Image التي بنيناها في تجربة الجزء الثاني، ومجال 203.0.113.0/24 مخصص للأمثلة والتوثيق، فنستخدمه ليمثل الإنترنت. وكل الأوامر من مجلد فيه ملف Dockerfile من الجزء الثاني وملف t.sh الذي فيه دالة الاختبار.
الإنترنت والبيت وال VPS
نبني شبكتين: الإنترنت، وشبكة السيرفرات في البيت، ثم أربعة Containers: ال VPS، وRouter البيت بعنوان عام من جهة وشبكة السيرفرات من الجهة الأخرى، وسيرفر البيت، وزائراً على الإنترنت:
docker build -q -t lab-hlp-tools:1 .
docker network create --subnet 203.0.113.0/24 --gateway 203.0.113.254 \
-o com.docker.network.bridge.enable_ip_masquerade=false lab-hlp-inet > /dev/null
docker network create --subnet 10.10.20.0/24 --gateway 10.10.20.254 \
-o com.docker.network.bridge.enable_ip_masquerade=false lab-hlp-srv > /dev/null
# the VPS on the internet
docker run -d --name lab-hlp-edge --hostname edge --cap-add NET_ADMIN \
--network lab-hlp-inet --ip 203.0.113.10 lab-hlp-tools:1 sleep 14400 > /dev/null
# the home router: a public address on one side, the servers VLAN on the other
docker run -d --name lab-hlp-router --hostname router --cap-add NET_ADMIN \
--sysctl net.ipv4.ip_forward=1 --network lab-hlp-inet --ip 203.0.113.77 \
lab-hlp-tools:1 sleep 14400 > /dev/null
docker network connect --ip 10.10.20.1 lab-hlp-srv lab-hlp-router
# the home server, and a visitor somewhere on the internet
docker run -d --name lab-hlp-app --hostname app --cap-add NET_ADMIN \
--network lab-hlp-srv --ip 10.10.20.21 lab-hlp-tools:1 sleep 14400 > /dev/null
docker run -d --name lab-hlp-visitor --hostname visitor \
--network lab-hlp-inet --ip 203.0.113.50 lab-hlp-tools:1 sleep 14400 > /dev/null
docker exec lab-hlp-app ip route replace default via 10.10.20.1
docker exec lab-hlp-router sh -c '
ip link set eth0 down; ip link set eth0 name wan; ip link set wan up
ip link set eth1 down; ip link set eth1 name srv; ip link set srv up
ip -br -4 addr'والمخرج سوف يكون كما يلي:
sha256:c081692f66f5d5cdc084def34fd3344b00cb90f55385e4a8a0a8b5c08b57b9ac
lo UNKNOWN 127.0.0.1/8
wan@if3769 UP 203.0.113.77/24
srv@if3770 UP 10.10.20.1/24 ثم نعطي Router البيت قواعد بيت عادي: كل ما يخرج من البيت مسموح مع NAT، ولا يوجد أي توجيه منفذ Port Forward من الإنترنت إلى الداخل، في الملف router.nft:
flush ruleset
table inet router {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" accept
iifname "srv" accept
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname "srv" oifname "wan" accept
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat;
oifname "wan" masquerade
}
}ونكتب التطبيق في البيت كبرنامج CGI صغير يطبع من أين جاءه الاتصال ورأس X-Forwarded-For، في الملف index.cgi:
#!/bin/sh
echo "Content-Type: text/plain"
echo
echo "hello from $(hostname) (10.10.20.21) inside the house"
echo "connection came from: $(echo "$REMOTE_ADDR" | sed 's/^\[::ffff://; s/\]$//')"
echo "X-Forwarded-For: $HTTP_X_FORWARDED_FOR"ثم نطبق القواعد ونشغل التطبيق على 8080، وخدمة ثانية على 22 تمثل SSH على نفس السيرفر، ونطلب التطبيق من داخل البيت:
docker cp router.nft lab-hlp-router:/etc/router.nft
docker exec lab-hlp-router nft -f /etc/router.nft
docker exec lab-hlp-app mkdir -p /srv/www/cgi-bin /srv/ssh
docker cp index.cgi lab-hlp-app:/srv/www/cgi-bin/index.cgi
docker exec lab-hlp-app sh -c 'chmod +x /srv/www/cgi-bin/index.cgi && httpd -p 8080 -h /srv/www'
# a second service on the same server that must never be reachable from the VPS
docker exec lab-hlp-app sh -c 'echo ssh > /srv/ssh/index.html && httpd -p 22 -h /srv/ssh'
docker exec lab-hlp-router curl -s http://10.10.20.21:8080/والمخرج سوف يكون كما يلي:
hello from app (10.10.20.21) inside the house
connection came from: 10.10.20.1
X-Forwarded-For: النفق يبدأ من البيت
الآن ننشئ مفاتيح WireGuard في الطرفين، ولاحظ أن كل طرف لا يقبل من الآخر إلا عنوان نفقه في AllowedIPs، وأن طرف البيت هو الذي يعرف عنوان ال VPS في Endpoint، وأن ال VPS لا يعرف عنوان البيت:
docker exec lab-hlp-edge sh -c 'umask 077; wg genkey | tee /etc/wireguard/edge.key | wg pubkey > /etc/wireguard/edge.pub'
docker exec lab-hlp-app sh -c 'umask 077; wg genkey | tee /etc/wireguard/app.key | wg pubkey > /etc/wireguard/app.pub'
EDGE_PUB=$(docker exec lab-hlp-edge cat /etc/wireguard/edge.pub)
APP_PUB=$(docker exec lab-hlp-app cat /etc/wireguard/app.pub)
docker exec -i lab-hlp-edge sh -c 'cat > /etc/wireguard/wg0.conf' <<EOF
[Interface]
Address = 10.10.99.1/24
ListenPort = 51820
PostUp = wg set %i private-key /etc/wireguard/edge.key
[Peer]
# the home server: only its tunnel address, nothing else from the house
PublicKey = $APP_PUB
AllowedIPs = 10.10.99.2/32
EOF
docker exec -i lab-hlp-app sh -c 'cat > /etc/wireguard/wg0.conf' <<EOF
[Interface]
Address = 10.10.99.2/24
PostUp = wg set %i private-key /etc/wireguard/app.key
[Peer]
# the VPS
PublicKey = $EDGE_PUB
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.10.99.1/32
PersistentKeepalive = 25
EOF
docker exec lab-hlp-edge wg-quick up wg0 > /dev/null 2>&1
docker exec lab-hlp-app wg-quick up wg0 > /dev/null 2>&1
sleep 2
docker exec lab-hlp-edge wg show wg0والمخرج سوف يكون كما يلي:
interface: wg0
public key: HOtSc8eKoW+sobD2TOmo/eyuv4GZ0loT/CYU0KsyB1A=
private key: (hidden)
listening port: 51820
peer: SxLY44JDedv73Y7wuGYvb3B16qtWsVI6E830n9LO6GQ=
endpoint: 203.0.113.77:43148
allowed ips: 10.10.99.2/32
latest handshake: 3 seconds ago
transfer: 180 B received, 92 B sentفي المخرج أعلاه لاحظ أن endpoint في ال VPS هو 203.0.113.77، أي العنوان العام ل Router البيت بعد NAT، وليس عنوان السيرفر، وهذا هو سبب أن هذه الطريقة تعمل خلف NAT وCGNAT: البيت هو الذي بدأ، وال VPS يرد على العنوان الذي جاءه منه.
جدار ناري على طرف البيت
النفق وحده يعني أن ال VPS يستطيع أن يصل إلى كل منفذ في سيرفر البيت، فإذا اخترق ال VPS فقد وصل المخترق إلى SSH في بيتك، لذلك نكتب على واجهة النفق في سيرفر البيت قاعدة تقبل من ال VPS المنفذ 8080 فقط، في الملف app.nft:
flush ruleset
table inet app {
chain input {
type filter hook input priority filter; policy accept;
iifname "wg0" ct state established,related accept
iifname "wg0" ip saddr 10.10.99.1 tcp dport 8080 accept
iifname "wg0" counter drop comment "anything else from the VPS"
}
}docker cp app.nft lab-hlp-app:/etc/app.nft
docker exec lab-hlp-app nft -f /etc/app.nft
docker exec lab-hlp-edge curl -s http://10.10.99.2:8080/والمخرج سوف يكون كما يلي:
hello from app (10.10.20.21) inside the house
connection came from: 10.10.99.1
X-Forwarded-For: التطبيق يرى الاتصال قادماً من 10.10.99.1، أي من ال VPS عبر النفق.
ال Reverse Proxy على ال VPS
الآن نشغل Caddy على ال VPS، في نفس شبكة lab-hlp-edge، ويمرر cloud.example.com إلى عنوان التطبيق داخل النفق، في الملف Caddyfile:
{
# the lab has no real domain, so Caddy signs with its own local CA;
# on a real VPS remove this line and Caddy gets a Let's Encrypt certificate
local_certs
}
cloud.example.com {
reverse_proxy 10.10.99.2:8080
}الخيار local_certs بحسب صفحة الخيارات العامة يجعل Caddy يصدر الشهادات من سلطته الداخلية بدلاً من Let's Encrypt، ونحتاجه لأن التجربة ليس لها نطاق حقيقي، وعلى VPS حقيقي تحذفه فيأخذ Caddy شهادة Let's Encrypt تلقائياً. ثم نشغله ونجعل الزائر يثق بسلطته كما يثق المتصفح ب Let's Encrypt، ونطلب الخدمة:
docker create --name lab-hlp-edge-caddy --network container:lab-hlp-edge caddy:2.11.7-alpine > /dev/null
docker cp Caddyfile lab-hlp-edge-caddy:/etc/caddy/Caddyfile
docker start lab-hlp-edge-caddy > /dev/null
sleep 5
# the visitor trusts the lab CA, as a browser trusts Let's Encrypt
docker exec lab-hlp-edge-caddy cat /data/caddy/pki/authorities/local/root.crt \
| docker exec -i lab-hlp-visitor sh -c 'cat > /root/lab-root.crt'
docker exec lab-hlp-visitor curl -sS --cacert /root/lab-root.crt \
--resolve cloud.example.com:443:203.0.113.10 https://cloud.example.com/والمخرج سوف يكون كما يلي:
hello from app (10.10.20.21) inside the house
connection came from: 10.10.99.1
X-Forwarded-For: 203.0.113.50في المخرج أعلاه لاحظ التالي:
- الزائر على الإنترنت وصل إلى سيرفر في البيت، وسيرفر البيت ليس له أي منفذ مفتوح في Router البيت.
- التطبيق يرى الاتصال من
10.10.99.1، وعنوان الزائر الحقيقي203.0.113.50في رأسX-Forwarded-Forالذي أضافه Caddy، وصفحةreverse_proxyتقول إنه يضع هذا الرأس أو يضيف إليه، وكيف يقرأ تطبيقك هذا الرأس بأمان هو موضوع تطبيقك خلف Reverse Proxy.
ماذا يرى المخترق لو أخذ ال VPS؟
نتحقق من الشهادة، ثم نجرب من الإنترنت الوصول إلى البيت مباشرة، ثم نتصرف كأننا مخترق على ال VPS ونجرب ما يستطيع الوصول إليه:
. ./t.sh
docker exec lab-hlp-visitor curl -sv --cacert /root/lab-root.crt \
--resolve cloud.example.com:443:203.0.113.10 https://cloud.example.com/ 2>&1 \
| grep -E 'SSL connection|issuer:|HTTP/2 200'
t visitor 203.0.113.77 8080
t visitor 203.0.113.77 22
t edge 10.10.99.2 8080
t edge 10.10.99.2 22
t edge 10.10.20.21 8080
docker exec lab-hlp-edge ip route get 10.10.20.21
docker exec lab-hlp-app nft list chain inet app input | grep counterوالمخرج سوف يكون كما يلي:
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / X25519MLKEM768 / id-ecPublicKey
* issuer: CN=Caddy Local Authority - ECC Intermediate
< HTTP/2 200
visitor -> 203.0.113.77 8080 blocked
visitor -> 203.0.113.77 22 blocked
edge -> 10.10.99.2 8080 open
edge -> 10.10.99.2 22 blocked
edge -> 10.10.20.21 8080 blocked
10.10.20.21 via 203.0.113.254 dev eth0 src 203.0.113.10 uid 0
cache
iifname "wg0" counter packets 2 bytes 120 drop comment "anything else from the VPS"في المخرج أعلاه لاحظ التالي:
- الاتصال بالزائر TLS 1.3، والشهادة من سلطة Caddy الداخلية لأننا في تجربة.
- من الإنترنت لا يوجد أي منفذ مفتوح على عنوان البيت العام، لا 8080 ولا 22.
- من ال VPS يصل المخترق إلى التطبيق على 8080 فقط، وSSH على نفس السيرفر ممنوع بقاعدة طرف البيت، والعداد يبين الحزم التي سقطت.
- من ال VPS لا يوجد طريق إلى عنوان السيرفر الحقيقي
10.10.20.21ولا إلى أي جهاز في شبكة البيت، والمسار يذهب إلى الإنترنت وليس إلى النفق.
وقد يتساءل البعض: ماذا لو أضاف المخترق مساراً إلى شبكة البيت عبر النفق بنفسه؟ والإجابة في المحاولة التالية:
docker exec lab-hlp-edge ip route add 10.10.20.0/24 dev wg0
docker exec lab-hlp-edge ping -c 1 -W 2 10.10.20.21
docker exec lab-hlp-edge ip route del 10.10.20.0/24 dev wg0والمخرج سوف يكون كما يلي:
PING 10.10.20.21 (10.10.20.21): 56 data bytes
ping: sendto: Required key not availableهذا هو التوجيه بالمفاتيح Cryptokey Routing في WireGuard: الحزمة إلى 10.10.20.21 لا تطابق AllowedIPs لأي طرف في إعداد ال VPS، لذلك يرفض WireGuard إرسالها أصلاً، والخطأ Required key not available يعني لا يوجد مفتاح لهذا العنوان. وحتى لو غير المخترق إعداد ال VPS نفسه، فطرف البيت لا يقبل منه إلا 10.10.99.1، وجدار البيت لا يقبل إلا 8080، وهذا هو الدفاع في العمق Defense in Depth: ثلاث طبقات، وكل واحدة تكفي وحدها.
عندما يتوقف البيت
الآن نوقف النفق في البيت، كأن الكهرباء انقطعت، ونرى ما يراه العميل، ثم نعيده:
docker exec lab-hlp-app wg-quick down wg0 > /dev/null 2>&1
docker exec lab-hlp-visitor curl -s -o /dev/null -w 'HTTP %{http_code} in %{time_total}s\n' \
--cacert /root/lab-root.crt --resolve cloud.example.com:443:203.0.113.10 https://cloud.example.com/
docker logs lab-hlp-edge-caddy 2>&1 | grep '"status":502' | tail -1 | grep -oE '"(logger|msg|status)":[^,]*'
docker exec lab-hlp-app wg-quick up wg0 > /dev/null 2>&1
sleep 3
docker exec lab-hlp-visitor curl -s -o /dev/null -w 'HTTP %{http_code} in %{time_total}s\n' \
--cacert /root/lab-root.crt --resolve cloud.example.com:443:203.0.113.10 https://cloud.example.com/والمخرج سوف يكون كما يلي:
HTTP 502 in 3.008416s
"logger":"http.log.error"
"msg":"dial tcp 10.10.99.2:8080: i/o timeout"
"status":502
HTTP 200 in 0.022563sالعميل أخذ 502 بعد ثلاث ثوان، والسبب في سجل Caddy واضح: لم يستطع الوصول إلى 10.10.99.2:8080. وعندما عاد النفق عادت الخدمة دون أي تدخل في ال VPS، لأن البيت هو الذي يبدأ الاتصال من جديد. وهذا هو الثمن الذي ذكرناه في الجزء الأول بالأرقام: في الطريقة A توقف البيت هو توقف الخدمة، لذلك ضع على ال VPS صفحة خطأ مفهومة للعميل بدلاً من 502 العارية، وراقب النفق من خارج البيت كما سيأتي.
التنظيف
نحذف ما أنشأناه فقط، بالاسم:
docker rm -f lab-hlp-edge-caddy lab-hlp-edge lab-hlp-router lab-hlp-app lab-hlp-visitor > /dev/null
docker network rm lab-hlp-inet lab-hlp-srv > /dev/null
docker rmi lab-hlp-tools:1 > /dev/null
docker ps -a --filter name=lab-hlp- --format '{{.Names}}'; docker network ls --filter name=lab-hlp- --format '{{.Name}}'ولا يظهر أي مخرج، لأن الأمرين الأخيرين لا يجدان شيئاً باسم lab-hlp-.
المراقبة والتنبيه
في بيئة الإنتاج لا تنتظر أن يخبرك أحد أن الخدمة متوقفة، والمراقبة في هذا البيت لها ثلاثة أسئلة، ولكل سؤال أداة:
- هل الخدمة تجيب؟ Uptime Kuma بترخيص MIT، وآخر إصدار عند كتابة المقال 2.5.5، يفحص كل خدمة كما يراها المستخدم: صفحة HTTPS، ومنفذ، واسم DNS، وشهادة قريبة من الانتهاء.
- هل الأجهزة بخير؟ Prometheus بترخيص Apache-2.0، وآخر إصدار v3.15.0، يسحب المقاييس Metrics من كل جهاز كل فترة: المعالج والذاكرة والقرص ودرجة الحرارة، وقواعد التنبيه فيه ترسل إلى Alertmanager الذي يجمع التنبيهات ويرسلها.
- كيف أرى الصورة؟ Grafana بترخيص AGPL-3.0، وآخر إصدار v13.2.3، يعرض المقاييس والسجلات في لوحات.
والتنبيه كله يصل إلى الهاتف عبر ntfy الذي تستضيفه بنفسك، والصورة التالية تبين من أين تأتي كل معلومة وإلى أين تذهب:

في الصورة أعلاه لاحظ التالي:
- ابدأ ب Uptime Kuma وحده، فهو يجيب عن السؤال الأهم في ساعة واحدة من الإعداد، وأضف Prometheus وGrafana عندما تريد أن تعرف لماذا توقفت الخدمة، وليس فقط أنها توقفت.
- المراقبة في البيت لا تستطيع أن تخبرك أن البيت كله توقف، لأنها توقفت معه، لذلك ضع نسخة صغيرة من Uptime Kuma على ال VPS تفحص النفق والخدمة من الخارج، وترسل إلى ntfy أو إلى بريدك.
- لا تنبه على كل شيء: التنبيه الذي يأتي كل يوم دون سبب يجعلك تتجاهل التنبيه الحقيقي، فابدأ بالتوقف، وامتلاء القرص فوق نسبة تحددها، وانتهاء الشهادة، وتشغيل ال UPS على البطارية من NUT.
السجلات المركزية
عندما يتوقف شيء في الثالثة فجراً، فأنت تريد سجلات كل الأجهزة في مكان واحد، وليس أن تدخل إلى سبعة أجهزة لتقرأ سبعة ملفات. وما هي السجلات ومستوياتها وكيف تكتبها شرحناه في السجلات Logging للمطورين ومديري السيرفرات، وفي هذا البيت نجمعها في Loki بترخيص AGPL-3.0، وآخر إصدار v3.7.8، في LXC مستقل، وكل جهاز يرسل إليه: الأجهزة الافتراضية من Docker، وOPNsense وال Switch عبر syslog، وProxmox نفسه. والسبب في أن يكون مستقلاً عن جهاز التطبيقات أن السجلات تهمك أكثر ما تهمك عندما يكون جهاز التطبيقات معطلاً.
النسخ الاحتياطي بقاعدة 3-2-1
القاعدة نفسها، والفرق بين RPO وRTO، وال Immutability، وتجربة استعادة حقيقية بأداة restic، كلها في النسخ الاحتياطي بقاعدة 3-2-1 واختبار الاستعادة، والمقارنة بين الأدوات في restic أم Borg أم Kopia، والصورة التالية تبين كيف نطبقها على هذا البيت:

- الأجهزة الافتراضية: Proxmox Backup Server مصمم لنسخ أجهزة Proxmox، وتوثيقه يذكر أنه يدعم إزالة التكرار Deduplication والنسخ التزايدي Incremental، وأن النسخة يمكن أن تشفر عند العميل قبل أن ترسل، وهذا مهم للنسخة خارج البيت.
- الملفات والصور على ال NAS: restic أو ما يقدمه نظام ال NAS، إلى نفس الهدف أو هدف آخر.
- خارج البيت: مخزن S3 أو VPS ثان، مشفر، ولا يستطيع سيرفر البيت أن يحذف منه، والسبب أن برنامج الفدية الذي يصل إلى البيت سيحاول حذف النسخ أولاً.
- سرعة الرفع: كما حسبنا في الجزء الأول، رفع 1 TB بسرعة 20 Mbps يحتاج قرابة 4.6 أيام، لذلك اجعل النسخة الأولى الكاملة في نهاية أسبوع، أو انقلها على قرص بيدك، ثم تأتي النسخ التالية بالفرق فقط.
- في الطريقة B: البيت هو النسخة خارج الموقع لل VPS، ويسحبها بنفسه، فلا يحتاج ال VPS أي صلاحية على البيت.
التحديثات ونوافذ الصيانة
بيئة الإنتاج لا تحدث كل شيء في نفس اللحظة، ولا تحدث يوم السبت قبل سفرك. والنمط الذي نوصي به في البيت كما يلي:
- نافذة صيانة ثابتة: مساء يوم محدد في الأسبوع، تعرفه العائلة والفريق، فإذا توقف شيء في ذلك الوقت فهم يعرفون السبب.
- Snapshot قبل كل تحديث: لكل جهاز افتراضي ستحدثه، فإذا فشل التحديث عدت في دقيقة.
- جهاز واحد في كل مرة: تحدث، ثم تنظر إلى Uptime Kuma، ثم تنتقل إلى الذي بعده.
- الترتيب: الخدمات أولاً، ثم Proxmox، ثم OPNsense في النهاية، لأن تحديث الجدار الناري يقطع الإنترنت ويقطع اتصالك عبر WireGuard إن كنت خارج البيت، فلا تحدثه إلا وأنت في البيت.
- اقرأ ملاحظات الإصدار قبل أي ترقية إلى إصدار رئيسي، وسجل ما فعلته في الويكي.
- التحديثات الأمنية العاجلة لا تنتظر النافذة، وخاصة ما على ال VPS، لأنه الجهاز المكشوف للإنترنت.
التوثيق وأدلة التشغيل
في الجزء الأول تكلمنا عن توثيق الخزانة والكابلات، وهنا نوثق التشغيل، والمكان الطبيعي هو BookStack أو أي ويكي داخلي، مع نسخة مطبوعة أو على هاتفك لأهم صفحة، والسبب أن الويكي نفسه في البيت، فإذا توقف البيت توقف الويكي معه. ودليل التشغيل Runbook هو صفحة لكل مهمة متكررة أو عطل متوقع، فيها:
- متى تستخدمه، أي العرض الذي تراه: «الإنترنت متوقف في كل البيت».
- الخطوات بالترتيب، بالأوامر الكاملة، وأين تكتب كل أمر.
- كيف تعرف أن المشكلة انحلت.
- متى حدثت آخر مرة، وماذا تعلمت منها.
وأهم أدلة التشغيل في هذا البيت: الإنترنت متوقف، والكهرباء انقطعت وعادت، واستعادة جهاز افتراضي من النسخة، واستعادة ملف حذفه أحد أفراد العائلة، وإضافة عضو في الفريق وحذفه من authentik وWireGuard، وتجديد شهادة يدوياً إذا فشل التجديد التلقائي. ولاحظ أن أول دليل يجب أن يكون الأبسط: أين الباسوردات، والمكان الذي يجب أن تكون فيه هو مدير باسوردات له نسخة خارج البيت، حتى يستطيع شخص آخر في العائلة أن يصل إليها إذا لم تكن موجوداً.
قائمة تجربة الكارثة
النسخة الاحتياطية التي لم تستعدها لا تعرف أنها تعمل، والإطفاء المنظم الذي لم تجربه لا تعرف أنه يطفئ، لذلك اختر يوماً كل بضعة أشهر في نافذة الصيانة، وجرب القائمة التالية، وسجل في الويكي كم استغرقت كل خطوة:
- افصل كهرباء ال UPS من المقبس، وتأكد أن NUT أطفأ ال NAS ثم Proxmox بالترتيب، وأن الأجهزة عادت وحدها عند عودة الكهرباء، وأن الخدمات كلها رجعت دون تدخل.
- استعد جهازاً افتراضياً كاملاً من Proxmox Backup Server إلى رقم جديد، وشغله في الشبكة المعزولة، وتأكد أن بياناته كاملة.
- استعد ملفاً من النسخة خارج البيت، وليس من النسخة المحلية، وقس كم أخذ.
- افصل كابل الإنترنت، وتأكد أن الخدمات الداخلية ما زالت تعمل بأسمائها، وأن DNS الداخلي يجيب، وأن المراقبة على ال VPS أرسلت تنبيهاً بأن البيت متوقف.
- ادخل من خارج البيت عبر WireGuard من هاتفك، وتأكد أنك تصل إلى شبكة الإدارة، وأن حساباً حذفته من الفريق لم يعد يستطيع الدخول.
- اطلب من فرد في العائلة أن يتبع دليل «الإنترنت متوقف» وحده، فإذا لم يستطع فالدليل يحتاج تعديلاً، وليس هو.
أخطاء شائعة
- Docker على سيرفر Proxmox نفسه: يخلط بين السيرفر الذي يشغل كل شيء وخدمة واحدة، وكل تحديث لأحدهما يخاطر بالآخر.
- نفق إلى ال VPS دون جدار ناري في طرف البيت: كما رأيت في التجربة، فالنفق وحده يعطي ال VPS كل منافذ السيرفر.
AllowedIPsواسع في ال VPS: شبكة البيت كلها في إعداد ال VPS تعني أن من أخذ ال VPS أخذ طريقاً إلى البيت.- المراقبة داخل البيت فقط: تتوقف مع البيت، فلا تعرف أن البيت توقف.
- نسخة احتياطية لم تستعد أبداً: وغالباً تكتشف المشكلة في يوم الكارثة نفسه.
- تحديث الجدار الناري وأنت مسافر: إذا لم يعد بعد إعادة التشغيل فقد أغلقت البيت على نفسك حتى تعود.
- الويكي هو المكان الوحيد للتوثيق: ويتوقف مع البيت في اللحظة التي تحتاجه فيها.
تدريب عملي
- أعد تشغيل التجربة، وأضف إلى
AllowedIPsفي طرف البيت شبكة10.10.20.0/24، ثم جرب من ال VPS الوصول إلى10.10.20.1، وفسر النتيجة بالطبقات الثلاث. - غير صفحة الخطأ في Caddy بالموجه handle_errors بحيث يرى العميل رسالة مفهومة بدلاً من 502، ثم أوقف النفق وتأكد.
الخلاصة
وصلنا لنهاية الموضوع ونهاية السلسلة، وأهم ما فيه:
- كل مجموعة خدمات في جهاز افتراضي مستقل فيه Docker، وLXC للخدمات الخفيفة، ولا شيء على سيرفر Proxmox نفسه، لأن توثيق Proxmox نفسه يوصي بتشغيل ال Containers داخل جهاز افتراضي لأعلى عزل.
- authentik أولاً، ثم كل خدمة داخلية خلف Reverse Proxy واحد بشهادة Let's Encrypt عبر التحقق من ال DNS، أو سلطة داخلية لما لا يحتاج اسماً عاماً.
- خدمة العميل على VPS، إما واجهة أمام البيت مع نفق يبدأ من البيت، وإما كاملة عليه والبيت يحمل نسختها، وفي الحالتين لا يفتح البيت أي منفذ.
- التجربة بينت ثلاث طبقات تحمي البيت لو اخترق ال VPS:
AllowedIPsفي ال VPS، وAllowedIPsفي البيت، والجدار الناري على واجهة النفق، وبينت أن توقف البيت يعني 502 للعميل في الطريقة الهجينة. - المراقبة تنتهي بتنبيه على الهاتف، وجزء منها خارج البيت، والسجلات في مكان واحد مستقل.
- 3-2-1 مع نسخة مشفرة خارج البيت لا يستطيع البيت حذفها، وتحديثات في نافذة ثابتة بعد Snapshot، وأدلة تشغيل لها نسخة خارج البيت، وتجربة كارثة كل بضعة أشهر.
وفي مقالات قادمة بإذن الله، نكمل مرحلة الهوم لاب بالخدمات التي تعيش فوق هذه البنية واحدة بعد الأخرى.
سجل التحديثات
- أكتوبر 2026: كتابة المقال، ومراجعة توثيق Proxmox VE لل LXC Containers، وصفحة أنواع التحقق في Let's Encrypt، وتوثيق Caddy للخيارات العامة وreverse_proxy، وتوثيق Proxmox Backup Server، وتراخيص وإصدارات authentik وUptime Kuma وPrometheus وGrafana وLoki وstep-ca، وتنفيذ التجربة العملية على Docker Engine 29.8.0 مع alpine:3.24 وwireguard-tools 1.0.20260223 وnftables 1.1.6 وcaddy:2.11.7-alpine.