في الساعة الأولى على خادم VPS جديد بنظام Ubuntu أو Debian، أغلق الأبواب التي تجربها الماسحات الآلية بعد دقائق من ظهور عنوان IP على الشبكة. حدّث النظام، وادخل بمستخدم عادي يملك صلاحيات sudo ومفتاح Ed25519، وعطّل الدخول بكلمة المرور والدخول بحساب root عبر ملف drop-in خاص بـ SSH، ثم فعّل جدارا ناريا يمنع كل شيء افتراضيا ولا يفتح إلا SSH ومنافذ الويب، ويفضل أن تكون مفتوحة لشبكة CDN وحدها. بعد ذلك فعّل تحديثات الأمان التلقائية مع إعادة تشغيل مجدولة، وأضف fail2ban أو CrowdSec، واعزل التطبيق داخل systemd، وجهّز النسخ الاحتياطية والتنبيهات قبل أن يشير DNS إلى الخادم.
تعتمد الأوامر على Ubuntu 24.04 LTS مرجعا، وتذكر مواضع اختلاف Debian 12 و13. أبقِ وحدة التحكم الويب لدى مزود الخدمة مفتوحة أثناء العمل، فهي طريق العودة إذا أغلق تعديل في SSH أو الجدار الناري الباب في وجهك.
حدّث النظام وأنشئ مستخدما بصلاحيات sudo
يسلّم كثير من المزودين دخولا بحساب root وكلمة مرور، لذلك استبدل الاثنين أولا. طبّق التحديثات المعلقة، ثم أنشئ حسابا شخصيا بصلاحيات sudo:
apt update && apt full-upgrade -y
adduser deploy
usermod -aG sudo deploy
تحتوي صور Ubuntu السحابية أصلا على حساب ubuntu بصلاحيات sudo. أما في تثبيت Debian الأدنى الذي عُيّنت فيه كلمة مرور لحساب root، فقد لا تكون حزمة sudo موجودة، لذا نفّذ apt install sudo أولا. أعط كل شخص حسابا باسمه حتى يشير سجل التدقيق إلى إنسان محدد. يسهّل التوقيت العالمي UTC مطابقة السجلات بين الخوادم ولوحات CDN: timedatectl set-timezone Etc/UTC.
فيمَ يختلف Debian عن Ubuntu 24.04
| المجال | Ubuntu 24.04 LTS | Debian 12 و13 |
|---|---|---|
| مستمع SSH | ssh.socket يشغّل الخدمة عند الطلب |
غالبا ssh.service؛ تحقق عبر systemctl status |
| الجدار الناري | ufw مثبت لكنه غير مفعّل |
لا شيء مفعّل؛ nftables هو الإطار الافتراضي |
| التحديثات التلقائية | unattended-upgrades مثبت ومفعّل |
قد يكون غائبا؛ ثبّته وفعّله |
| سجل المصادقة | /var/log/auth.log وسجل systemd |
سجل systemd فقط؛ لا يُثبَّت rsyslog افتراضيا منذ Debian 12 |
sudo |
مثبت | قد يكون غائبا في التثبيت الأدنى |
| OpenSSH | 9.6 | 9.2 في Debian 12، و10.0 في Debian 13 |
SSH بمفاتيح Ed25519 وملف drop-in للتقوية
أنشئ المفتاح وثبّته
أنشئ المفتاح على جهاز عملك، ولا تنشئه على الخادم أبدا. مفاتيح Ed25519 قصيرة وسريعة وتدعمها كل إصدارات OpenSSH الحالية. احمِ المفتاح الخاص بعبارة مرور واتركه في ssh-agent. وإذا كان لديك مفتاح أمان FIDO2، فإن ssh-keygen -t ed25519-sk يربط المفتاح بالعتاد.
ssh-keygen -t ed25519 -C "deploy@laptop-2026"
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
يحتاج ssh-copy-id إلى دخول واحد بكلمة المرور. إذا كان root يقبل مفتاحك أصلا وكانت كلمات المرور معطلة، فانسخ ملف المفاتيح على الخادم نفسه:
sudo install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
sudo install -m 600 -o deploy -g deploy /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
الأذونات مهمة: عندما يكون StrictModes مفعّلا، وهو الوضع الافتراضي، يتجاهل sshd ملفات المفاتيح التي يستطيع مستخدمون آخرون الكتابة فيها.
اكتب ملف drop-in
يبدأ الملف /etc/ssh/sshd_config في Ubuntu بالسطر Include /etc/ssh/sshd_config.d/*.conf، ويذكر دليل sshd_config أن أول قيمة يحصل عليها الخادم لكل كلمة مفتاحية هي التي تُستخدم. تُقرأ ملفات drop-in بالترتيب المعجمي وتتقدم على بقية الملف الرئيسي. وغالبا ما تأتي الصور السحابية بملف خاص بها مثل 50-cloud-init.conf أو 60-cloudimg-settings.conf يضبط PasswordAuthentication، لذلك أعطِ ملفك بادئة منخفضة:
# /etc/ssh/sshd_config.d/00-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
AllowUsers deploy
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no
يغلق KbdInteractiveAuthentication no مسار keyboard-interactive عبر PAM، وهو مسار قد يطلب كلمة مرور حتى مع تعطيل PasswordAuthentication. ويحصر AllowUsers الدخول في حسابات محددة بالاسم. يضبط الملف الرئيسي في Ubuntu الخيار X11Forwarding yes، ويتغلب عليه ملف drop-in لأنه يُقرأ أولا. وإذا لم يحتج أحد إلى إعادة التوجيه، فأضف AllowAgentForwarding no وAllowTcpForwarding no.
تحقق من الصياغة، واعرض القيم الفعلية، ثم طبّق الإعدادات:
sudo sshd -t
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauthentication|kbdinteractive|authenticationmethods|allowusers'
sudo systemctl restart ssh
يكتشف sshd -t أخطاء الصياغة، ويعرض sshd -T الإعدادات المطبقة فعلا، بما في ذلك ما إذا كان ملف cloud-init قد تغلب على ملفك. إعادة تشغيل ssh تحافظ على الجلسات القائمة. في Ubuntu 24.04 يعود مقبس الاستماع إلى ssh.socket، لذا يتطلب تغيير Port أو ListenAddress أيضا تنفيذ sudo systemctl daemon-reload وsudo systemctl restart ssh.socket، بعد أن يسمح الجدار الناري بالمنفذ الجديد. ويغطي دليل OpenSSH server من Ubuntu بقية الخيارات.
اختبر في جلسة ثانية قبل إغلاق الأولى
أبقِ الجلسة الحالية مفتوحة. من طرفية جديدة، ادخل باسم deploy بالمفتاح ونفّذ sudo -v. ثم تأكد من أن المسارات المغلقة تفشل:
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password [email protected]
ssh [email protected]
يجب أن تنتهي المحاولتان بالرسالة Permission denied (publickey). لا تغلق الجلسة الأصلية إلا بعد ذلك. نقل SSH إلى منفذ آخر يقلل الضجيج في السجلات، لكنه ليس ضابطا أمنيا.
جدار ناري يمنع افتراضيا: ufw أو nftables
الجدار الناري على المضيف هو الطبقة الثانية بعد الجدار الناري الشبكي لدى المزود، إن وُجد. أسقط كل حركة المرور الواردة ما عدا SSH ومنافذ الويب، واسمح بالحركة الصادرة، وغطِّ IPv6 كما تغطي IPv4.
Ubuntu: ufw
أضف قاعدة SSH قبل تفعيل الجدار الناري، وإلا فسيقطع ufw enable جلستك:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw limit OpenSSH
sudo ufw allow proto tcp from any to any port 80,443 comment 'web'
sudo ufw enable
sudo ufw status verbose
وفقا لـدليل ufw، تحظر قاعدة limit العنوان الذي يفتح 6 اتصالات أو أكثر خلال 30 ثانية. هذا يبطئ الماسحات، لكنه قد يعطل الأتمتة التي تفتح اتصالات SSH متوازية كثيرة، وهنا يناسب allow البسيط أكثر. إذا كان لديك عنوان إداري ثابت أو شبكة VPN، فاسمح بـ SSH منه فقط: sudo ufw allow from 198.51.100.7 to any port 22 proto tcp. تنطبق القواعد على IPv6 أيضا ما دام IPV6=yes مضبوطا في /etc/default/ufw، وهي القيمة الافتراضية.
تحذّر وثائق Docker من أن المنافذ التي ينشرها Docker تتجاوز قواعد ufw وfirewalld. انشر الحاويات على عنوان loopback، مثل -p 127.0.0.1:3000:3000، واجعل الوكيل العكسي هو الواجهة المكشوفة للإنترنت.
Debian: nftables
يعتمد Debian على nftables إطارا للجدار الناري. يطبّق الملف /etc/nftables.conf التالي السياسة نفسها ويجهّز مجموعات مسماة لنطاقات CDN في القسم التالي:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set cloudflare_v4 {
type ipv4_addr
flags interval
}
set cloudflare_v6 {
type ipv6_addr
flags interval
}
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif "lo" accept
meta l4proto { icmp, ipv6-icmp } accept
tcp dport 22 accept
ip saddr @cloudflare_v4 tcp dport { 80, 443 } accept
ip6 saddr @cloudflare_v6 tcp dport { 80, 443 } accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
}
افحص الملف بالأمر sudo nft -c -f /etc/nftables.conf، ثم نفّذ sudo systemctl enable --now nftables. أبقِ ICMPv6 مفتوحا، لأن اكتشاف الجيران واكتشاف MTU المسار في IPv6 يعتمدان عليه. يحذف flush ruleset أيضا الجداول التي أنشأها fail2ban أو Docker، لذا أعد تشغيلهما بعد إعادة التحميل. واستخدم أداة واحدة لإدارة الجدار الناري على كل مضيف.
اسمح لشبكة CDN وحدها بالوصول إلى منافذ الويب
إذا كان الموقع خلف Cloudflare أو شبكة CDN أخرى، فلا ينبغي أن يرد الخادم الأصلي على أي جهة أخرى في المنفذين 80 و443. وإلا فإن كل من يعثر على عنوان الخادم الأصلي، عبر سجلات DNS قديمة أو خادم بريد على العنوان نفسه أو مسح شامل للإنترنت، يستطيع تجاوز جدار WAF وحدود الطلبات والتخزين المؤقت لدى CDN. تنشر Cloudflare نطاقاتها على https://www.cloudflare.com/ips-v4 وhttps://www.cloudflare.com/ips-v6، وتذكر وثائق عناوين IP أن النطاقات الجديدة تُضاف إلى القائمة قبل استخدامها في الإنتاج.
في ufw أضف قاعدة لكل نطاق. لا تنتهي الملفات بسطر جديد، لذلك يجب أن تعالج الحلقة السطر الأخير صراحة، وإلا فستتخطى نطاقا دون أي تنبيه:
#!/usr/bin/env bash
set -euo pipefail
for list in ips-v4 ips-v6; do
curl -fsS "https://www.cloudflare.com/$list" |
while read -r cidr || [ -n "$cidr" ]; do
ufw allow proto tcp from "$cidr" to any port 80,443 comment 'cloudflare'
done
done
بعد ذلك احذف القاعدة المفتوحة بالأمر sudo ufw delete allow proto tcp from any to any port 80,443. أما في nftables فحدّث المجموعتين في معاملة واحدة، لأن nft -f يطبق الدفعة كاملة أو لا يطبق شيئا. وإذا فشل التنزيل، يوقف set -e السكربت وتبقى المجموعات القديمة كما هي:
#!/usr/bin/env bash
set -euo pipefail
v4=$(curl -fsS https://www.cloudflare.com/ips-v4 | paste -sd, -)
v6=$(curl -fsS https://www.cloudflare.com/ips-v6 | paste -sd, -)
nft -f - <<EOF
flush set inet filter cloudflare_v4
flush set inet filter cloudflare_v6
add element inet filter cloudflare_v4 { $v4 }
add element inet filter cloudflare_v6 { $v6 }
EOF
شغّله عند الإقلاع بعد nftables.service، ثم أسبوعيا عبر مؤقت systemd. يحتاج Nginx بعد ذلك إلى set_real_ip_from للنطاقات نفسها وإلى real_ip_header CF-Connecting-IP;، وإلا سترى السجلات وحدود الطلبات عناوين Cloudflare بدلا من عناوين الزوار. ويغطي دليل التخزين المؤقت مع Nginx وCloudflare جانب خادم الويب.
تثبت قائمة عناوين IP المسموح بها أن الاتصال قادم من شبكة Cloudflare، لكنها لا تثبت أنه يخص منطقتك. ويصف دليل Cloudflare لحماية الخادم الأصلي هذه القائمة بأنها عرضة لانتحال عناوين IP، ويعرض خيارات أقوى: Authenticated Origin Pulls التي تتطلب وضع التشفير Full أو Full (strict)، وCloudflare Tunnel الذي لا يحتاج إلى أي منفذ ويب وارد على الإطلاق.
التحديثات التلقائية وإعادة تشغيل النواة
يثبّت Ubuntu Server حزمة unattended-upgrades ويفعّل تحديثات الأمان افتراضيا، كما يشرح دليل التحديثات التلقائية. تحقق من ذلك:
cat /etc/apt/apt.conf.d/20auto-upgrades
sudo unattended-upgrade --dry-run -v
يجب أن يحتوي الملف على APT::Periodic::Update-Package-Lists "1"; وAPT::Periodic::Unattended-Upgrade "1";. وفي Debian نفّذ sudo apt install unattended-upgrades ثم sudo dpkg-reconfigure -plow unattended-upgrades.
لا تسري إصلاحات النواة إلا بعد إعادة التشغيل. في Ubuntu 24.04 يعيد needrestart تشغيل الخدمات التي تستخدم مكتبات محدّثة، لكنه لا يستطيع استبدال النواة العاملة. وعندما تحتاج حزمة إلى إعادة تشغيل يظهر الملف /var/run/reboot-required. ضع إعداداتك المحلية في ملف يأتي ترتيبه بعد الملف 50unattended-upgrades الذي توفره الحزمة:
// /etc/apt/apt.conf.d/52unattended-upgrades-local
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:30";
اختر وقتا قليل الحركة، وتأكد من أن التطبيق يعود بعد الإقلاع دون خطوات يدوية. توجد السجلات في /var/log/unattended-upgrades/. يصلح Canonical Livepatch، المجاني للاستخدام الشخصي على خمسة أجهزة كحد أقصى، ثغرات النواة الحرجة والعالية في الذاكرة مباشرة، لكن Canonical تؤكد أنه لا يغني عن إعادة التشغيل.
fail2ban أو CrowdSec
مع تعطيل الدخول بكلمة المرور، لا يمكن لهجوم التخمين على SSH أن ينجح. أداة الحظر طبقة ثانية تقلل ضجيج السجلات، وتوفر وقت المعالج المستهلك في مصافحات الاتصال، وتبطئ الماسحات. اختر واحدة منهما.
يتوفر fail2ban في مستودعات التوزيعتين ولا يتطلب أي حساب. في Ubuntu 24.04 تفعّل الحزمة سجن sshd مع الواجهة الخلفية لسجل systemd وإجراءات nftables، وهذه الواجهة تناسب Debian أيضا لأنه لا يملك auth.log. ثبّته بالأمر sudo apt install fail2ban، ثم اضبط السجن في ملف محلي بدلا من jail.conf:
# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h
bantime.increment = true
طبّق الملف بالأمر sudo systemctl restart fail2ban وافحص حالة السجن بالأمر sudo fail2ban-client status sshd.
يحلل CrowdSec السجلات نفسها، ويتبادل الإشارات مع قائمة حظر مجتمعية، وينفذ قراراته عبر مكوّن معالجة منفصل. يأتي Ubuntu 24.04 بالإصدار 1.4.6، بينما يطلب دليل تثبيت CrowdSec إصدارا أحدث من مستودع المطوّر. اقرأ سكربت الإعداد قبل تشغيله:
curl -s https://install.crowdsec.net | sudo sh
sudo apt install crowdsec crowdsec-firewall-bouncer-nftables
sudo cscli collections list
sudo cscli decisions list
خلف شبكة CDN تصل طلبات الويب من عناوين CDN. لذلك فإن حظر مهاجم HTTP في جدار المضيف الناري إما أن يقطع عقدة من عقد Cloudflare، وإما أن يستهدف عنوانا لا يتصل مباشرة أبدا. أبقِ الحظر على مستوى المضيف لـ SSH، واستخدم قواعد WAF وتحديد معدل الطلبات في CDN لحركة HTTP.
اعزل التطبيق باستخدام systemd
شغّل الواجهة الخلفية بمستخدم نظام خاص بها داخل وحدة systemd، لا داخل صدفة ولا عبر nohup، ودع systemd ينزع من العملية كل ما لا تحتاج إليه. أنشئ المستخدم بالأمر sudo useradd --system --no-create-home --shell /usr/sbin/nologin webapp. تشغّل هذه الوحدة خدمة Node.js على localhost خلف Nginx:
# /etc/systemd/system/webapp.service
[Unit]
Description=Example web application
After=network-online.target
Wants=network-online.target
[Service]
User=webapp
Group=webapp
WorkingDirectory=/opt/webapp
ExecStart=/usr/bin/node /opt/webapp/server.js
Environment=HOST=127.0.0.1 PORT=3000
LoadCredential=db_password:/etc/webapp/db_password
Restart=on-failure
StateDirectory=webapp
UMask=0077
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
ProtectProc=invisible
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
LockPersonality=yes
CapabilityBoundingSet=
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
[Install]
WantedBy=multi-user.target
يمنع NoNewPrivileges=yes العملية وأبناءها من اكتساب صلاحيات عبر ملفات setuid أو قدرات الملفات. ويركّب ProtectSystem=strict نظام الملفات للقراءة فقط بالنسبة إلى الخدمة، باستثناء /dev و/proc و/sys، فيصبح المكان الوحيد القابل للكتابة هو /var/lib/webapp الذي ينشئه StateDirectory=. أضف ReadWritePaths= لأي مسار آخر. يخفي ProtectHome=yes المجلدات /home و/root و/run/user، ويمنح PrivateTmp=yes الخدمة مجلدي /tmp و/var/tmp خاصين بها. يزيل CapabilityBoundingSet= الفارغ كل القدرات، أما @system-service فهي قائمة الاستدعاءات المسموح بها التي يوصي بها دليل systemd.exec نقطة انطلاق.
أُسقط الخيار MemoryDenyWriteExecute=yes عمدا: يذكر الدليل أنه لا يتوافق مع محركات JIT، ومحرك V8 في Node.js يولّد الشيفرة أثناء التشغيل. أضفه إلى الخدمات المكتوبة بلغات Go أو Rust أو C ثم اختبرها.
على Ubuntu 24.04 مع systemd 255، أعطى الأمر systemd-analyze security --offline=true هذا الملف تقييم 1.5، مقابل 9.0 للوحدة نفسها عند الاكتفاء بـ User=. شغّل الخدمة وتحقق:
sudo systemctl daemon-reload
sudo systemctl enable --now webapp
systemd-analyze security webapp.service
journalctl -u webapp -b -n 50 --no-pager
إذا فشلت الخدمة في الإقلاع، فعادة ما يذكر السجل المسار أو استدعاء النظام المحظور. خفّف خيارا واحدا في كل مرة بدلا من حذف الكتلة كلها. تبدأ الخدمات المحزّمة مثل Nginx بصلاحيات root وتحتاج إلى قدرات محددة، لذا قوّها عبر sudo systemctl edit nginx خيارا بعد خيار أيضا.
مزامنة الوقت وjournald والاحتفاظ بالسجلات
يعتمد التحقق من TLS ورموز TOTP وجداول النسخ الاحتياطي ومطابقة السجلات على ساعة دقيقة. يستخدم Ubuntu 24.04 وDebian 12 افتراضيا systemd-timesyncd. نفّذ timedatectl status وابحث عن System clock synchronized: yes وNTP service: active. وإذا لم تكن هناك خدمة NTP عاملة، فثبّت systemd-timesyncd أو chrony.
مع القيمة الافتراضية Storage=auto لا يُحفظ السجل على القرص إلا إذا كان /var/log/journal موجودا، وقد يشغل حتى 10 في المئة من نظام الملفات بحد أقصى 4 غيغابايت. اضبط حدودا صريحة كي لا تملأ خدمة كثيرة الضجيج قرصا صغيرا، وكي تتوافق مدة الاحتفاظ مع سياسة الخصوصية لديك:
# /etc/systemd/journald.conf.d/50-retention.conf
[Journal]
Storage=persistent
SystemMaxUse=1G
MaxRetentionSec=30day
طبّقه بالأمر sudo systemctl restart systemd-journald وتحقق عبر journalctl --disk-usage. تتولى أداة logrotate تدوير سجلات Nginx والتطبيق في /var/log، لذا اضبط /etc/logrotate.d/ على المدة نفسها. وتؤكد ملاحظات إصدار Debian 12 أن rsyslog لم يعد يُثبَّت افتراضيا، فيكون سجل systemd هو سجل النظام الوحيد في Debian. تحتوي سجلات الوصول على عناوين IP قد تُعد بيانات شخصية بموجب اللائحة العامة لحماية البيانات GDPR، لذلك تكون مدة الاحتفاظ الأقصر هي الصحيحة في أغلب الأحيان.
الأسرار وأذونات الملفات
تتسرب الأسرار على خادم VPS عادة من طرق مألوفة: ملف .env يستطيع كل المستخدمين قراءته، أو مفتاح في سجل Git، أو متغير بيئة طُبع في تقرير عطل. ينص دليل systemd على أن متغيرات البيئة لا تصلح للأسرار، لأنها مكشوفة للعملاء غير المميزين عبر D-Bus وترثها العمليات الأبناء. استخدم LoadCredential= كما في الوحدة أعلاه. يبقى الملف المصدر مقروءا لحساب root وحده، وتحصل الخدمة على نسخة للقراءة فقط في $CREDENTIALS_DIRECTORY:
sudo install -d -m 0700 -o root -g root /etc/webapp
sudo install -m 0600 -o root -g root /dev/null /etc/webapp/db_password
sudoedit /etc/webapp/db_password
ويحفظ systemd-creds encrypt مع LoadCredentialEncrypted= الملف مشفرا أثناء التخزين بمفتاح المضيف أو بشريحة TPM. يجب أن تكون شيفرة التطبيق مملوكة لحساب root أو لمستخدم النشر، لا لمستخدم الخدمة، حتى لا تستطيع عملية مخترقة إعادة كتابة نفسها. بعد تثبيت برمجيات من طرف ثالث، ابحث عن الملفات القابلة للكتابة من الجميع وعن ملفات setuid غير المتوقعة:
sudo find / -xdev -type f -perm -0002 -print
sudo find / -xdev -type f -perm -4000 -print
خصص لمفتاح النشر في CI سطرا مستقلا في authorized_keys مع الخيار restrict، وأضف إن أمكن قائمة عناوين from=. ويحتاج وكلاء البرمجة الذين ينفذون أوامر على الخادم إلى الانضباط نفسه: مستخدم مستقل، ولا وصول إلى أسرار الإنتاج، وموافقة على الأوامر المدمرة. ويقارن دليل البرمجة بالوكلاء ضوابط الأذونات في أدوات CLI الحالية.
النسخ الاحتياطية ونقاط ربط المراقبة
تقلل التقوية احتمال وقوع حادث، أما النسخ الاحتياطية والتنبيهات فتحدد مدة استمراره. لقطات المزود مفيدة للتراجع السريع، لكنها تعيش في الحساب نفسه مع الخادم، فلا تُعد نسخة مستقلة. احتفظ بنسخة مشفرة ذات إصدارات خارج المزود واختبر الاستعادة. ويشرح دليل النسخ الاحتياطي 3-2-1 باستخدام restic بنية المستودع ومدة الاحتفاظ وتمارين الاستعادة.
يحتاج خادم VPS منفرد إلى بضع إشارات: فحص HTTP خارجي عبر CDN، وفحص يتأكد من أن الخادم الأصلي يرفض الاتصالات المباشرة، ومساحة القرص، وانتهاء صلاحية الشهادات، ووحدات systemd الفاشلة، ونبضة heartbeat من كل مهمة مجدولة، حتى تُطلق النسخة الاحتياطية التي تتوقف بصمت تنبيها. يستطيع systemd استدعاء أداة إشعار كلما فشلت وحدة:
# /etc/systemd/system/[email protected]
[Unit]
Description=Alert on failure of %i
[Service]
Type=oneshot
ExecStart=/usr/local/bin/notify-failure %i
أضف OnFailure=notify-failure@%n.service إلى قسم [Unit] في وحدات التطبيق والنسخ الاحتياطي. يمكن للسكربت أن يرسل اسم الوحدة وآخر أسطر السجل إلى webhook لمنصة محادثة أو نظام مناوبة. ويجب أن تستمع مصدّرات المقاييس، مثل Prometheus node exporter، على localhost أو شبكة خاصة. وإذا كان الخادم يشغّل وكلاء ذكاء اصطناعي أو أدوات MCP تملك وصولا إلى الصدفة، فخصص لها مستخدما وعزلا خاصين، وتعامل مع مدخلاتها على أنها غير موثوقة، كما يوضح دليل حقن التعليمات وأمان MCP.
قائمة التحقق للساعة الأولى
- طبّق التحديثات، وأنشئ مستخدم sudo باسم صاحبه، وأبقِ وحدة تحكم المزود مفتوحة.
- ثبّت مفتاح Ed25519 وأضف
00-hardening.confمع تعطيل الدخول بحساب root وبكلمة المرور. - نفّذ
sshd -tوsshd -T، وأعد تشغيلssh، واختبر جلسة جديدة قبل إغلاق القديمة. - فعّل جدارا ناريا يمنع افتراضيا في IPv4 وIPv6 ويسمح بـ SSH و80 و443.
- اقصر منافذ الويب على نطاقات Cloudflare أو استخدم Authenticated Origin Pulls أو Tunnel، واستعد عناوين IP الحقيقية للزوار في Nginx.
- تأكد من تحديثات الأمان التلقائية وجدول إعادة التشغيل التلقائية.
- فعّل fail2ban أو CrowdSec لـ SSH، واستخدم WAF لدى CDN لحركة HTTP.
- شغّل التطبيق بمستخدم خاص داخل وحدة systemd معزولة، وتحقق من
systemd-analyze security. - تأكد من مزامنة الوقت واضبط حدود حجم السجل ومدة الاحتفاظ به.
- انقل الأسرار إلى ملفات credentials لا يقرؤها إلا root، وافحص أذونات الملفات.
- أعدّ نسخا احتياطية خارج المزود، واختبر الاستعادة، وأضف تنبيهات الأعطال ونبضات heartbeat.
- أعد تشغيل الخادم مرة واحدة عمدا، وتأكد من عودة كل الخدمات دون خطوات يدوية.