কোন systemd স্যান্ডবক্স আপনার OpenVPN হুক প্রত্যাখ্যান করেছে তা কীভাবে খুঁজবেন
হুকটি root হিসেবে চলে এবং systemd-এর অধীনে ব্যর্থ হয়, কারণ unit কম সক্ষমতা, ছোট /dev এবং কেবল-পঠনযোগ্য পথ ধরে রাখে।
হুকটি আপনি root হিসেবে হাতে চালালে কাজ করে এবং ফিরিয়ে দেয় Operation not permitted সেই মুহূর্তে, যখন সার্ভিসটি এটি চালু করে। এই দুই চালনার মধ্যে ফাইলটি বদলায়নি; পরিবেশ বদলেছে। systemd প্রসেসটিকে দেয় ছোট এক জগৎ: কম সক্ষমতা, ভিন্ন একটি /dev, কেবল-পঠনযোগ্য ফাইলসিস্টেম পথ এবং একটি টাস্ক সীমা। ব্যর্থতাটিকে পড়ুন এমন সীমা হিসেবে যা একটি নির্দিষ্ট অপারেশন প্রত্যাখ্যান করেছে, ভাঙা স্ক্রিপ্ট হিসেবে নয়।
যে অপারেশনটি প্রত্যাখ্যাত হয়েছিল সেখান থেকেই শুরু করুন
unit ব্যর্থ হয়েছে বলে জানানো লগ লাইন আপনাকে প্রায় কিছুই বলে না। কাজের লাইনটি প্রত্যাখ্যাত কলটির নাম বলে: যে ডিভাইস নোড খোলা যায়নি, যে ডিরেক্টরিতে লেখা যায়নি, যে namespace তৈরি করা যায়নি। একটিও ডিরেক্টিভ স্পর্শ করার আগে সেই কলটি খুঁজুন, কারণ প্রত্যেকটি ভিন্ন সীমাবদ্ধতার সঙ্গে মেলে। তারপর প্যাকেজ যে ফাইল দিয়েছে তা নয়, বাস্তবে চলা unit-টি পড়ুন, drop-in সহ।
নিচের কুয়েরিটি ফ্র্যাগমেন্ট পথ, 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 শুরু হয়েছে, হুক প্রয়োজনীয় বিশেষাধিকার নিয়ে চলেছে তা নয়।
namespace-এর জন্য mount অধিকার দরকার, যা TUN ডিভাইস কখনো ব্যবহার করে না
একই হুকের ভিতরে একই রকম দেখানো দুটি অপারেশনের অনুমতি-প্রোফাইল আলাদা। /dev/net/tun খোলা এবং ইন্টারফেস তৈরি করতে দরকার নেটওয়ার্ক প্রশাসন এবং ওই নোডে প্রবেশাধিকার। নামযুক্ত নেটওয়ার্ক namespace তৈরি করতেও mount-সংক্রান্ত কাজ দরকার, কারণ namespace টুলটি একটি নতুন /sys ভিউ মাউন্ট করে এবং একটি স্টেট ডিরেক্টরি জায়গামতো বাঁধে। আপস্ট্রিম ক্লায়েন্ট unit সচেতনভাবেই তার bounding set-এর বাইরে mount প্রশাসন রাখে, তাই namespace ধাপটি প্রত্যাখ্যাত হয়, অথচ টানেল নিজে কাজ করত।
এই কারণেই ডিভাইস নোড দেওয়া স্ক্রিপ্টটিকে বাঁচায় না। ডিভাইস অনুমতি একটিমাত্র প্রশ্নের উত্তর দেয়, এই প্রসেস এই নোড খুলতে পারে কি না, আর namespace-এর প্রশ্নটি অন্য একটি। কিছু প্রশস্ত করার আগে ব্যর্থ কলটিকে তার সক্ষমতার সঙ্গে মিলিয়ে নিন।
আপনার শেলে করা পরীক্ষা একটি অনিয়ন্ত্রিত root-এর সব সক্ষমতা উত্তরাধিকারসূত্রে পায়, আর unit পায় তার bounding set যা রেখেছে তা। দুই চালনা দুটি ভিন্ন পরীক্ষা এমনকি বাইনারিটি অভিন্ন হলেও।
ডিভাইস, সুরক্ষিত পথ এবং টাস্ক সীমা
প্রত্যাখ্যাত অপারেশন জানা হয়ে গেলে তিনটি ডিরেক্টিভ-গোষ্ঠী বেশিরভাগ ব্যর্থতা ব্যাখ্যা করে। ডিভাইস অ্যাক্সেস নিয়ন্ত্রণ করে PrivateDevices, যা প্রতিস্থাপন করে /dev কে এক ন্যূনতম সিউডো-ডিভাইস সেট দিয়ে, এবং DevicePolicy সঙ্গে DeviceAllow, যারা ঠিক করে cgroup কী অনুমতি দেয়। /dev/net/tun ওই ন্যূনতম সেটের অংশ নয়, তাই PrivateDevices=yes যুক্ত unit ডিভাইস নীতি যা-ই বলুক এটি খুলতে পারে না।
ফাইলসিস্টেম সুরক্ষা দ্বিতীয় প্রশ্নের উত্তর দেয়। ProtectHome=yes তৈরি করে /home, /root এবং /run/user অগম্য, আর ProtectSystem=full তৈরি করে /usr, /boot এবং /etc কেবল-পঠনযোগ্য। তাই যে হুক নিজের কনফিগারেশন ফাইলের পাশে লেখে, সেটি আপনার শেলে কাজ করে এবং unit-এর অধীনে ব্যর্থ হয়, অনুপস্থিত সক্ষমতার মতো একই অনুমতি-আকৃতি নিয়ে। লেখার উপযোগী ইনপুটটি নীতিতে অনুমোদিত কোনো পথে সরান, বা একটি সংকীর্ণ ReadWritePaths= এন্ট্রি যোগ করুন।
রিসোর্স সীমা তৃতীয় গোষ্ঠী, এবং সবচেয়ে কম স্পষ্ট। TasksMax= ঠিক করে unit কতগুলো টাস্ক তৈরি করতে পারে, আর ফাইল-ডেসক্রিপ্টর সীমা সকেট ও হ্যান্ডেলের জন্য একইভাবে কাজ করে। সীমা ছাড়িয়ে বেড়ে যাওয়া ডিপ্লয়মেন্ট অনুমতি-ত্রুটির বদলে রিসোর্স-ত্রুটিতে ব্যর্থ হয়, তাই সঠিক বার্তাটি এখনও গুরুত্বপূর্ণ।
namespace ধাপটি হুকের বাইরে আনুন
যে প্রস্তুতির জন্য এমন বিশেষাধিকার দরকার যা ক্লায়েন্টের থাকা উচিত নয়, তা তার নিজের unit-এ থাকা উচিত। ক্লায়েন্টের আগে চলা, নিজের ফল ধরে রাখা এবং সংযোগের আগে ক্রমবদ্ধ একটি oneshot namespace ধাপটিকে যা দরকার তা দেয়, ওই অধিকারগুলো টানেল প্রসেসকে না দিয়েই। এরপর ক্লায়েন্ট নিজের up হুক থেকে স্ক্রিপ্ট ডাকার বদলে ওই unit-কে রেফার করে।
নিচের টেমপ্লেটটি একটি শুরুর আকৃতি, না পড়ে প্রয়োগ করার মতো কিছু নয়। এটি প্রস্তুতিকে সেখানেই রাখে যেখানে বিশেষাধিকার থাকে, এবং টানেল 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প্যাকেজ করা unit সম্পাদনার বদলে drop-in দিয়ে ক্রমটি যোগ করুন, যাতে কোনো আপগ্রেড আপনার পরিবর্তন চুপচাপ ফেলে না দেয়। ডিরেক্টরিটির চাবি ইনস্ট্যান্সের নাম, তাই দ্বিতীয় প্রোফাইলের নিজের ফাইল দরকার:
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 চালু হয় অথচ হুক আর কখনো চলে না, সেটি মেরামত নয়।
নিচের ক্রমটি তুলনাটিকে সৎ রাখে, কারণ এটি 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শেষ লাইন পড়ার বদলে প্রত্যাখ্যানগুলো গুনুন, কারণ একটি হুক ব্যর্থ হতে পারে, আবার চেষ্টা করতে পারে এবং সফল হতে পারে, আর সীমা সরেেছে কি না তা কেবল গণনাই দেখায়। drop-in-এ আপনি যে ডিরেক্টিভটি শিথিল করেছেন তার নাম ও কারণ লিখে রাখুন, কারণ পরের আপগ্রেড আপনার কনফিগারেশনকে প্যাকেজ করা ডিফল্টের সঙ্গে তুলনা করে। যে unit সব সুরক্ষা বন্ধ করার পরেই কেবল চালু হয়, সেটি সারানো হয়নি।