سرور پر انتظامی پینل تقریباً ہمیشہ اسی دیوار سے ٹکراتا ہے: ویب پروسیس کو کچھ ایسا کرنا ہوتا ہے جس کا اُسے حق نہیں۔ کوئی پتہ بلاک کرنا، کوئی پورٹ کھولنا، جرنل پڑھنا۔ جو جواب خود سامنے آتا ہے وہ یہ ہے کہ sudoers میں ایک تنگ سطر دے دی جائے اور اس سے زیادہ کچھ نہیں:

www-data ALL=(root) NOPASSWD: /usr/bin/fail2ban-client set * banip *

سطر یوں پڑھی جاتی ہے: «صرف بلاک کرنا»۔ حقیقت میں اس کا مطلب ہے: «root کے طور پر کچھ بھی»۔ ذیل میں یہ ہے کہ ایسا کیوں ہوتا ہے، اسے اپنے سرور پر پانچ منٹ میں کیسے جانچیں، اور ایسے قاعدے کس چیز سے بدلے جاتے ہیں۔

* اصل میں کیا کرتا ہے

فیصلہ کن تفصیل: sudo دلائل کا ایک ایک سے موازنہ نہیں کرتا بلکہ پوری کمانڈ لائن کا، اور نمونے میں موجود * خالی جگہوں سے — یعنی دلائل کی سرحدوں سے — بہ آسانی گزر جاتا ہے۔ اسی لیے قاعدے کے وسط میں کھڑا لفظ banip کسی چیز کو محدود نہیں کرتا: کافی ہے کہ لفظ banip کمانڈ میں کہیں آ جائے، اور اس سے پہلے بھی اور بعد میں بھی کچھ بھی بھیجا جا سکتا ہے۔

اس کے بعد ایسا پروگرام تلاش کیا جاتا ہے جسے چلانے کے لیے کوئی کمانڈ سونپی جا سکے۔ ہمارے معاملے میں وہ خود اسی قاعدے میں موجود تھا۔ fail2ban میں بلاک کرنے کا عمل متن کی صورت میں دیا جاتا ہے، اور اس متن کو چلتے چلتے از سرِ نو متعین کیا جا سکتا ہے:

fail2ban-client get portscan actions
sudo fail2ban-client set portscan action nftables-multiport actionban "<کمانڈ>" banip 192.0.2.10
sudo fail2ban-client set portscan banip 192.0.2.11

پہلی کال کسی حقیقی عمل کا متن بدل دیتی ہے، دوسری jail کو چلنے پر مجبور کرتی ہے — اور fail2ban، جو root کے طور پر چلتا ہے، ڈالی گئی چیز کو انجام دے دیتا ہے۔ ہم نے یہ 4 جولائی کو اپنے ہی سرور پر کیا: /root میں ایک فائل root کے حق سے بن گئی، یعنی ویب پروسیس کو ایسے قاعدے سے مشین پر مکمل حقوق مل گئے جو تنگ دکھائی دیتا تھا۔

راستے میں تین باتیں سامنے آئیں جو دوسروں کے تجزیوں میں عموماً نہیں ملتیں:

  • set سے پہلے -c کو sudo کاٹ دیتا ہے: نمونے میں set براہِ راست بائنری کے نام سے جُڑا ہوا ہے اور اس سے پہلے کچھ ڈالا نہیں جا سکتا؛
  • banip کے بغیر addaction اور action بھی نہیں گزرتے — لیکن جو کچھ درکار ہے وہ ایک ہی کمانڈ set … banip … میں سما جاتا ہے، اور یوں نمونہ پورا ہو جاتا ہے؛
  • عمل کا نام حقیقی ہونا چاہیے، ورنہ بدلنے کو کچھ نہیں ہوتا: اسے fail2ban-client get <jail> actions دکھاتا ہے۔

اسی فہرست میں اور کون ہے

fail2ban یہاں قصوروار نہیں اور نہ ہی واحد مثال ہے۔ خطرناک NOPASSWD اور ستارے کا ملاپ خود ہے۔ اپنے سرورز پر اس کے ساتھ ہمیں کیا ملا:

  • journalctl * — جرنل ایک پیجر کے ذریعے کھلتا ہے، اور پیجر سے شیل چل پڑتا ہے۔ قاعدہ بظاہر لاگ پڑھنے کا ہے؛ عملاً یہ root شیل ہے۔ علاج تنگ نمونہ نہیں بلکہ گروپ systemd-journal ہے: پھر journalctl بغیر sudo ہی کام کرتا ہے؛
  • grep * /var/log/fail2ban.log — grep کی پہلی دلیل نمونہ ہے، مگر ستارہ دوسرا راستہ بھی دینے کی اجازت دیتا ہے، اور grep root کے طور پر چلتا ہے۔ اس قاعدے سے /etc/shadow پڑھنا ایک ہی کمانڈ ہے۔ متبادل وہی ہے: لاگ پڑھنے کے لیے گروپ adm؛
  • ufw --force * اور نرے ufw allow/deny/delete * — ویب پروسیس فائر وال کو پورا بند کر سکتا ہے۔ خاص طور پر ناخوشگوار: دونوں صورتوں میں پینل نے یہ قاعدے استعمال ہی نہیں کیے تھے؛ یہ انسٹالر کے ابتدائی نسخوں سے بچی ہوئی مردہ اجازتیں تھیں؛
  • lynis-scan.sh * — ایسا ریپر جو "$@" کو root پروسیس تک پہنچاتا ہے، بغیر کسی حد والے قاعدے کے برابر ہے۔

اپنے ہاں کیا ہے، یہ کیسے دیکھیں

دیکھنے کی چیز فائل نہیں بلکہ ایک مخصوص صارف کے اصل حقوق ہیں — وہی صارف جس کے تحت PHP-FPM کا پول واقعی چلتا ہے (ہمیشہ www-data نہیں؛ ہماری ایک مشین پر پینل admin کے نام سے چل رہا تھا):

ps -o user= -C php-fpm | sort -u
sudo -l -U www-data
sudo -l -U admin

اس کے بعد خود فائلوں کی فہرست، اور یہاں دو پھندے ہیں جن پر ہمارا وقت لگا:

sudo grep -rn 'NOPASSWD' /etc/sudoers /etc/sudoers.d/
ls -la /etc/sudoers.d/

پہلا: قاعدے بیک وقت دو جگہوں پر رہتے ہیں۔ ہمارے ہاں خطرناک سطر /etc/sudoers.d/monitor میں بھی تھی اور مرکزی /etc/sudoers میں بھی — مرکزی میں تقریباً آٹھ نقلوں کی صورت، جو انسٹالر کے مختلف نسخوں نے جمع کر دی تھیں۔ ایک جگہ صاف کرکے مطمئن ہو جانا سوراخ کھلا چھوڑنے کا معمول کا طریقہ ہے۔

دوسرا: sudo اُن فائلوں کو نظرانداز کرتا ہے جن کے نام میں نقطہ ہو۔ /etc/sudoers.d/ میں موجود فائل www-data.bak3 نافذ قاعدے جیسی دکھتی ہے اور نافذ قاعدے کی طرح پڑھی جاتی ہے، مگر اثر نہیں رکھتی۔ یہ دونوں طرف چلتا ہے: «قاعدہ ہے، حق نہیں» اور یہ جھوٹی تسلی کہ ترتیب کی نقل «بس اسی کے پہلو میں پڑی ہے»۔

کس چیز سے بدلیں

نمونے کو تنگ کرنا بے سود ہے — سطر میں کہیں بھی ستارہ مسئلہ کو آغاز پر لوٹا دیتا ہے۔ ایک ہی طریقہ ٹھہرتا ہے: ایک root ریپر جو مقررہ ترتیب والے دلائل لیتا ہے، خود ان کی جانچ کرتا ہے اور پروگرام کو ایسے پکارتا ہے کہ کچھ جوڑنے کی ذرا بھی گنجائش نہ ہو۔

#!/bin/sh
# /usr/local/sbin/arciveo-f2b — ban|unban <jail> <ip>، اور اس سے زیادہ کچھ نہیں
set -eu
action="$1"; jail="$2"; ip="$3"
case "$action" in ban|unban) ;; *) echo "invalid action"; exit 1 ;; esac
case "$jail" in *[!a-zA-Z0-9_-]*|'') echo "invalid jail"; exit 1 ;; esac
printf '%s' "$ip" | grep -Eq '^[0-9a-fA-F:.]+$' || { echo "invalid ip"; exit 1; }

case "$action" in
  ban)   exec /usr/bin/fail2ban-client set "$jail" banip   "$ip" ;;
  unban) exec /usr/bin/fail2ban-client set "$jail" unbanip "$ip" ;;
esac

ریپر root کی ملکیت ہے، اجازتیں 755، اور — یہ اختیاری نہیں — یہ ویب سے قابلِ تحریر نہیں ہونا چاہیے، ورنہ پوری ساخت بے معنی ہو جاتی ہے۔ sudoers میں صرف یہی رہ جاتا ہے:

www-data ALL=(root) NOPASSWD: /usr/local/sbin/arciveo-f2b

قاعدے میں دلائل گنوائے نہیں جاتے: اُن کی جانچ خود اسکرپٹ کرتی ہے، اور اس میں مقامات مقرر ہیں، چنانچہ عمل کا متن کھسکانے کی کوئی جگہ نہیں۔ صرف پڑھنے والے قاعدے — حالتیں، ss، ipset list — الگ سطروں میں جاتے ہیں، وہاں بھی جہاں ممکن ہو بغیر ستارے۔

الگ سے، sudo کو گروپوں سے بدلنے کے بارے میں۔ طریقہ درست ہے: جرنل کے لیے systemd-journal اور لاگ کے لیے adm کسی بھی sudo قاعدے سے سستے اور محفوظ تر پڑتے ہیں۔ مگر ویب صارف کو گروپوں سے اندھا دھند نکالنا نہیں چاہیے۔ ہم نے پینل والے میزبان پر www-data کو ایک گروپ سے نکالا اور یکبار گی تمام سائٹوں پر 403 آ گیا: وہاں Apache www-data کے طور پر چلتا ہے، سائٹوں کی فائلیں دوسرے صارف کی ملکیت ہیں، اور پڑھنے کی رسائی بالکل اسی گروپ کی رکنیت سے آتی تھی۔ بحالی کے لیے Apache اور PHP-FPM کا مکمل ری اسٹارٹ درکار ہوا — ری لوڈ نہیں — کیونکہ پرانے ورکر اپنے سابقہ گروپ رکھے رہتے ہیں اور «چلتا ہے، پھر 403» کی حالت پیدا کرتے ہیں۔

تبدیلی کے بعد جانچ

sudoers کی فائل بدلنے سے پہلے اس کی ساخت جانچنا ضروری ہے — ورنہ بالکل بغیر sudo رہ جانے کا خطرہ ہے:

sudo visudo -cf /etc/sudoers.d/monitor

اس کے بعد تصدیق کرنی چاہیے کہ پرانا راستہ مر گیا ہے، اور وہ بھی اسی صارف کے نام سے:

sudo -k
sudo -u www-data sudo -n fail2ban-client set portscan banip 192.0.2.10

جواب a password is required ہونا چاہیے، جبکہ پتے کی جگہ کوڑا دے کر ریپر کو پکارنے پر invalid ip آنا چاہیے۔ یہاں اپنا ایک پھندا گھات میں ہے: sudo -v تصدیق کو پندرہ منٹ کے لیے محفوظ کر لیتا ہے، اور اس کے بعد sudo -n والی ہر جانچ «کامیابی سے» گزر جاتی ہے۔ بالکل اسی طرح ہم نے ایک بار ایسے قاعدے کی تصدیق کر لی تھی جو سرور پر موجود ہی نہیں تھا۔ اسی لیے جانچ سے پہلے sudo -k لازمی ہے۔

نگرانی کس لیے

sudo کے قاعدے کم بدلتے ہیں، مگر بے آواز بدلتے ہیں: انسٹالر ان میں اضافہ کرتا ہے، ہوسٹنگ کا پینل اپنا حصہ ڈالتا ہے، ہٹایا گیا پیکج اپنی سطریں پیچھے چھوڑ جاتا ہے۔ ایک بار کا جائزہ آج جو موجود ہے اسے بند کرتا ہے اور اس بارے میں کچھ نہیں بتاتا کہ اگلی اپ ڈیٹ کیا لائے گی۔ عملی نتیجہ سادہ ہے: کون root بن سکتا ہے، اس کی فہرست سرور کی باقی حالت کے ساتھ نظر کے سامنے رہنی چاہیے، نہ کہ کسی واقعے کے بعد یاد آنے والی چیز ہو۔ یہ سب یکجا ہو کر کیسا لگتا ہے، نیچے دیے گئے ڈیمو صفحات دکھاتے ہیں۔