कैसे पता करें कि किस 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 दृश्य mount करता है और स्टेट निर्देशिका को उसकी जगह बाँधता है। अपस्ट्रीम क्लाइंट unit जानबूझकर mount प्रशासन को अपने बाउंडिंग सेट से बाहर रखता है, इसलिए namespace चरण ठुकराया जाता है जबकि टनल स्वयं काम करता।
इसीलिए डिवाइस नोड देना स्क्रिप्ट को नहीं बचाता। डिवाइस अनुमति एक सवाल का जवाब देती है, क्या यह प्रक्रिया यह नोड खोल सकती है, और namespace का सवाल दूसरा है। कुछ भी चौड़ा करने से पहले विफल कॉल को उसकी क्षमता से मिलाएँ।
आपके शेल में किया गया परीक्षण एक असीमित root की हर क्षमता विरासत में पाता है, और unit वह पाता है जो उसके बाउंडिंग सेट ने रखा। दोनों रन अलग प्रयोग हैं तब भी जब बाइनरी एक जैसी हो।
डिवाइस, संरक्षित पथ और कार्य सीमाएँ
ठुकराई गई क्रिया ज्ञात होने के बाद, तीन निर्देश-समूह अधिकांश विफलताएँ समझाते हैं। डिवाइस तक पहुँच को नियंत्रित करता है 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 कितने कार्य बना सकता है, और फ़ाइल-डिस्क्रिप्टर की सीमा sockets और हैंडल के लिए वैसे ही व्यवहार करती है। सीमा से बढ़ा हुआ परिनियोजन अनुमति-त्रुटि के बजाय संसाधन-त्रुटि से विफल होता है, इसीलिए सटीक संदेश अब भी मायने रखता है।
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क्रम को 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 तब शुरू होता है जब हुक फिर कभी नहीं चलता, वह मरम्मत नहीं है।
नीचे का क्रम तुलना को ईमानदार रखता है, क्योंकि यह 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 सारी सुरक्षा बंद करने के बाद ही शुरू होता है, वह ठीक नहीं हुआ।