Hook کام کرتا ہے جب آپ اسے root کے طور پر ہاتھ سے چلاتے ہیں اور واپس کرتا ہے Operation not permitted اسی لمحے جب سروس اسے شروع کرتی ہے. فائل ان رنز کے درمیان تبدیل نہیں ہوئی؛ ماحول بدل گیا. systemd عمل کو ایک چھوٹی دنیا دیتا ہے: کم صلاحیتیں، مختلف /dev، صرف پڑھنے والے فائل سسٹم راستے اور کاموں کی حد. ناکامی کو ایک حد کے طور پر پڑھیں جس نے ایک مخصوص آپریشن مسترد کیا، نہ کہ ٹوٹی ہوئی اسکرپٹ کے طور پر.

اسی آپریشن سے شروع کریں جسے مسترد کیا گیا

unit ناکام ہونے والی لاگ لائن آپ کو تقریباً کچھ نہیں بتاتی. مفید لائن مسترد شدہ کال کا نام لیتی ہے: ایک ڈیوائس نوڈ جو کھولا نہ جا سکا، ایک ڈائریکٹری جس میں لکھا نہ جا سکا، ایک namespace جو بنایا نہ جا سکا. کسی ایک ہدایت کو چھونے سے پہلے وہ کال تلاش کریں، کیونکہ ہر ایک مختلف پابندی سے مطابقت رکھتی ہے. پھر وہ unit پڑھیں جو واقعی چلا، drop-in سمیت، اس فائل کے بجائے جو پیکیج نے بھیجی.

نیچے دیا گیا استفسار fragment کا راستہ، drop-in ڈائریکٹریاں اور وہ خصوصیات چھاپتا ہے جو ڈیوائس یا فائل سسٹم کی مسترد ہونے کے لیے اہم ہیں. یہ طے کرنے سے پہلے کہ کون سی حد متعلقہ ہے، سب پڑھیں:

systemctl show "[email protected]" -p FragmentPath -p DropInPaths -p PrivateDevices -p DevicePolicy -p DeviceAllow -p ProtectHome -p ProtectSystem -p TasksMax
journalctl -u "[email protected]" -b --no-pager | grep -iE 'operation not permitted|permission denied|cannot open|failed'

اس بلاک میں کچھ نہیں لکھتا. اگر مسترد شدہ کال جرنل میں موجود نہیں، تو ہدایات کا اندازہ لگانے کے بجائے ایک رن کے لیے کلائنٹ کی تفصیل بڑھائیں. چلتا ہوا عمل ثابت کرتا ہے کہ unit شروع ہوا، نہ کہ hook مطلوبہ مراعات کے ساتھ چلا.

namespace کو mount کے وہ حقوق درکار ہیں جو TUN ڈیوائس کبھی استعمال نہیں کرتا

ایک ہی hook کے اندر یکساں نظر آنے والے دو آپریشنز کے اجازت پروفائل مختلف ہوتے ہیں. کھولنا /dev/net/tun اور انٹرفیس بنانا نیٹ ورک ایڈمنسٹریشن کے علاوہ اس نوڈ تک رسائی کا تقاضا کرتا ہے. نامزد نیٹ ورک namespace بنانے کے لیے بھی mount سے متعلق کام درکار ہے، کیونکہ namespace ٹول تازہ /sys منظر mount کرتا ہے اور اسٹیٹ ڈائریکٹری کو اس کی جگہ باندھتا ہے. اپ اسٹریم کلائنٹ unit جان بوجھ کر mount ایڈمنسٹریشن کو اپنے bounding set سے باہر چھوڑ دیتا ہے، اس لیے namespace کا مرحلہ مسترد ہو جاتا ہے جبکہ سرنگ خود کام کرتی.

اسی لیے ڈیوائس نوڈ دینا اسکرپٹ کو نہیں بچاتا. ڈیوائس کی اجازت ایک سوال کا جواب دیتی ہے، کہ کیا یہ عمل اس نوڈ کو کھول سکتا ہے، اور namespace کا سوال دوسرا ہے. کچھ بھی وسیع کرنے سے پہلے ناکام کال کو اس کی صلاحیت سے ملائیں.

آپ کے shell میں ایک ٹیسٹ ہر وہ صلاحیت وراثت میں لیتا ہے جو ایک غیر محدود root کے پاس ہے، اور unit وہ وارث ہوتا ہے جو اس کے bounding set نے رکھا. وہ دو رنز مختلف تجربات ہیں چاہے بائنری ایک جیسی ہو.

ڈیوائسز، محفوظ راستے اور کاموں کی حدیں

جب مسترد شدہ آپریشن معلوم ہو جائے، تو تین گروہ ہدایات زیادہ تر ناکامیوں کی وضاحت کرتے ہیں. ڈیوائس تک رسائی کی نگرانی PrivateDevices کرتا ہے، جو بدلتا ہے /dev کو کم سے کم فرضی ڈیوائسز کے مجموعے سے، اور DevicePolicy کے ساتھ DeviceAllow، جو طے کرتے ہیں کہ cgroup کیا اجازت دیتا ہے. /dev/net/tun اس کم سے کم مجموعے کا حصہ نہیں، اس لیے ایک unit جس کے پاس PrivateDevices=yes ہے اسے نہیں کھول سکتا، چاہے ڈیوائس پالیسی کچھ کہے.

فائل سسٹم کا تحفظ دوسرے سوال کا جواب دیتا ہے. ProtectHome=yes /home, /root اور /run/user کو ناقابل رسائی بناتا ہے، اور ProtectSystem=full /usr, /boot اور /etc کو صرف پڑھنے کے لیے بناتا ہے. اس لیے وہ hook جو اپنی کنفیگریشن فائل کے پاس لکھتا ہے آپ کے shell میں کام کرتا ہے اور unit کے تحت ناکام ہوتا ہے، اسی اجازت کی شکل کے ساتھ جیسے کوئی صلاحیت غائب ہو. لکھنے کے قابل ان پٹ کو ایسے راستے پر منتقل کریں جس کی پالیسی اجازت دیتی ہے، یا ایک تنگ ReadWritePaths= اندراج شامل کریں.

وسائل کی حدیں تیسرا گروہ ہیں، اور سب سے کم واضح. TasksMax= حد مقرر کرتا ہے کہ unit کتنی ٹاسک بنا سکتا ہے، اور فائل ڈسکرپٹر کی حد ساکٹس اور ہینڈلز کے لیے اسی طرح برتاؤ کرتی ہے. وہ تعیناتی جو حد سے بڑھ گئی، اجازت کی خرابی کے بجائے وسیلے کی خرابی سے ناکام ہوتی ہے، اسی لیے درست پیغام اب بھی اہم ہے.

namespace کے مرحلے کو hook سے باہر نکالیں

وہ تیاری جو ایسی مراعت چاہتی ہے جو کلائنٹ کے پاس نہیں ہونی چاہیے، اپنے unit میں ہونی چاہیے. ایک oneshot جو کلائنٹ سے پہلے چلتا ہے، اپنا نتیجہ رکھتا ہے اور کنکشن سے پہلے ترتیب دیا جاتا ہے، namespace کے مرحلے کو وہ دیتا ہے جس کی اسے ضرورت ہے، بغیر وہ حقوق سرنگ کے عمل کو دیے. پھر کلائنٹ اس unit کا حوالہ دیتا ہے بجائے اس کے کہ اسکرپٹ کو اپنے up hook سے بلائے.

نیچے دیا گیا ٹیمپلیٹ ایک ابتدائی شکل ہے، ایسی چیز نہیں جسے پڑھے بغیر لاگو کیا جائے. یہ تیاری وہیں رکھتا ہے جہاں مراعت رہتی ہے اور سرنگ کے unit کو اچھوتا چھوڑتا ہے:

# /etc/systemd/system/[email protected]
[Unit]
Description=Namespace preparation for %i
Before=openvpn-client@%i.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/netns-up.sh %i
CapabilityBoundingSet=CAP_NET_ADMIN CAP_SYS_ADMIN
[Install]
WantedBy=multi-user.target

ترتیب کو drop-in کے ذریعے شامل کریں بجائے پیک شدہ unit میں ترمیم کے، تاکہ اپ گریڈ آپ کی تبدیلی کو خاموشی سے نہ گرا سکے. ڈائریکٹری مثال کے نام سے جڑی ہے، اس لیے دوسرے پروفائل کو اپنی فائل درکار ہے:

sudo mkdir -p /etc/systemd/system/[email protected]
sudo tee /etc/systemd/system/[email protected]/10-namespace.conf >/dev/null <<'CONF'
[Unit]
[email protected]
[email protected]
CONF
sudo systemctl daemon-reload

تبدیلی کے بعد وہی استدعا دہرائیں

مرمت کی تصدیق صرف اس وقت ہوتی ہے جب وہی استدعا مختلف نتیجہ دے. دونوں unit دوبارہ شروع کریں، وہ آپریشن چلائیں جو ناکام ہوا، اور اس کے نتیجے کا موازنہ تبدیلی سے پہلے محفوظ کی گئی جرنل لائن سے کریں. وہ unit جو شروع ہو جبکہ hook دوبارہ کبھی نہ چلے، مرمت نہیں ہے.

نیچے دیا گیا تسلسل موازنے کو ایماندار رکھتا ہے، کیونکہ یہ unit کے رنگ کے بجائے مسترد شدہ کال کی جانچ کرتا ہے. اس کا آؤٹ پٹ تبدیلی سے پہلے پکڑی گئی جرنل لائن کے پاس محفوظ کریں:

sudo systemctl restart "[email protected]" "[email protected]"
systemctl status "[email protected]" --no-pager
journalctl -u "[email protected]" -b --no-pager | grep -c 'Operation not permitted'
ip -brief addr show tun0

آخری لائن پڑھنے کے بجائے مسترد ہونے گنیں، کیونکہ hook ناکام ہو سکتا ہے، دوبارہ کوشش کر کے کامیاب ہو سکتا ہے، اور صرف گنتی دکھاتی ہے کہ حد ہلی یا نہیں. drop-in میں وہ ہدایت اور وجہ نام لکھ کر تبصرہ چھوڑیں جو آپ نے ڈھیلی کی، کیونکہ اگلا اپ گریڈ آپ کی کنفیگریشن کا موازنہ پیک شدہ ڈیفالٹ سے کرتا ہے. وہ unit جو ہر تحفظ بند ہونے کے بعد ہی شروع ہوتا ہے، ٹھیک نہیں ہوا.

متعلقہ مضامین