प्लगइन सैंडबॉक्स और ख़तरा मॉडल¶
सर्वर प्लगइन को सीमित रखने के लिए क्या करता है: इंस्टॉल के समय की जाँचें, चलते समय की
परतें, सैंडबॉक्स मोड, और हर एक अब भी क्या नहीं रोक सकता। कोड
internal/adapters/driven/pluginrunner (इंस्टॉल का विश्लेषण, सैंडबॉक्स),
cmd/evey-plugin-launcher (Landlock और kernel का seccomp फ़िल्टर), sandbox/sandbox.c
(LD_PRELOAD फ़िल्टर) और internal/adapters/driving/pluginhost (वह होस्ट API जिसे
प्लगइन कॉल करते हैं) में है; डिज़ाइन
ADR-0012,
ADR-0013 और
ADR-0014 तय करते हैं। अगर आप प्लगइन लिख रहे
हैं और जानना चाहते हैं कि आप क्या बता सकते हैं और क्या मना होगा, तो
अनुमतियाँ और स्लॉट पढ़ें; आपकी मशीन को कौन सा सैंडबॉक्स
मिलता है, यह जानने के लिए प्लगइन सैंडबॉक्स सेटअप पढ़ें।
ख़तरा मॉडल¶
प्लगइन अलग प्रोसेस के रूप में चलते हैं, जो Unix socket पर gRPC के ज़रिए सर्वर से संवाद करते हैं। सर्वर डिफ़ॉल्ट रूप से किसी प्लगइन कोड पर भरोसा नहीं करता। जिन ख़तरों से वह बचाता है, सबसे गंभीर से सबसे कम गंभीर तक:
- दूसरे की पहचान लेना - कोई प्लगइन दूसरे प्लगइन होने का दिखावा करता है ताकि उसकी अनुमतियाँ इस्तेमाल कर सके।
- अधिकार बढ़ाना - कोई प्लगइन अपने सैंडबॉक्स से बाहर निकल जाता है और सर्वर के अधिकारों से command चलाता है।
- फ़ाइल सिस्टम तक पहुँच - कोई प्लगइन अपने फ़ोल्डर के बाहर पढ़ता या लिखता है, ख़ासकर सर्वर के secret।
- नेटवर्क से डेटा बाहर भेजना - कोई प्लगइन अपनी पसंद के होस्ट को डेटा भेजता है।
- संसाधनों का दुरुपयोग - mining, fork bomb, मेमोरी या डिस्क ख़त्म कर देना।
- सप्लाई चेन - छिपाया गया या compiled कोड, जिसे विश्लेषण पढ़ नहीं सकता।
flowchart LR
zip[पैकेज] --> ex[ZIP extraction<br/>सीमाएँ, symlink नहीं, setuid नहीं]
ex --> man[Manifest<br/>अनुमतियाँ]
man --> an[स्थैतिक विश्लेषण<br/>CRITICAL मना करता है]
an --> sig[सिग्नेचर<br/>cosign नीति]
sig --> prev[एडमिन का पूर्वावलोकन<br/>स्वीकार या मना]
prev --> run[चलते समय]
subgraph run[चलते समय - हर मोड में]
fs[फ़ाइल सिस्टम<br/>सिर्फ़ अपना फ़ोल्डर]
net[नेटवर्क<br/>कोई IP socket नहीं, Fetch broker]
sys[सिस्टम कॉल<br/>seccomp मना-सूची]
uid[अलग उपयोगकर्ता<br/>या user namespace]
sock[होस्ट socket<br/>SO_PEERCRED से पहचान]
res[संसाधनों की निगरानी<br/>मेमोरी, PID, डिस्क, nice]
end
इंस्टॉल के समय¶
ZIP extraction¶
कोई भी फ़ाइल लिखे जाने से पहले (pluginrunner/zip.go):
- ज़्यादा से ज़्यादा 5,000 एंट्री, हर फ़ाइल 100 MB, कुल 500 MB बिना compression के, और 1 MB (बिना compression) से ऊपर 100:1 का compression ratio (decompression bomb से बचाव);
- जिन एंट्री का रास्ता आर्काइव से बाहर जाता है (
..), वे मना हैं; - symbolic link मना हैं;
- setuid, setgid और sticky bit हटा दिए जाते हैं।
Manifest और अनुमतियाँ¶
manifest वे अनुमतियाँ बताता है जो प्लगइन माँगता है। कोई अनजानी अनुमति इंस्टॉल मना करवा देती है। फिर इन्हें लागू किया जाता है:
subprocesschild process की अनुमति देती है। इसके बिना kernel प्रोसेस बनाना मना करता है (-deny-fork), और interpreter वाले प्लगइन के लिएLD_PRELOADफ़िल्टर भीexecveमना करता है।native-binarycompiled कोड या आर्काइव साथ लाने देती है, जिन्हें विश्लेषण पढ़ नहीं सकता।- API अनुमतियाँ (
api.library,api.user,api.metadata,media.read,media.write,notification.sender...) होस्ट की हर कॉल परPluginPermissionInterceptorजाँचता है।
पूरी सूची अनुमतियाँ और स्लॉट में है।
स्थैतिक विश्लेषण¶
पैकेज की हर फ़ाइल को
pluginrunner.AnalyzePlugin
जाँचता है, एक सामान्य बनाने वाले दौर के बाद जो आम तरह से छिपाए गए कोड को खोल देता है
(alias, string जोड़ना, f-string):
| श्रेणी | गंभीरता | उदाहरण |
|---|---|---|
shell_exec |
WARNING / CRITICAL | os.system, subprocess.run, child_process.exec, PHP system()/shell_exec()/proc_open(), preg_replace /e, Ruby Kernel.exec/system, open('\|cmd') (CRITICAL); Ruby backticks (WARNING) |
network |
WARNING / CRITICAL | requests.get, fetch, reverse और bind shell |
filesystem |
WARNING / CRITICAL | /etc/passwd, .ssh/, rm -rf, symlink के काम |
obfuscation |
WARNING / CRITICAL | eval, __builtins__, base64 -d \| bash और shell में भेजे जाने वाले दूसरे decode (CRITICAL); base64 decoding, hex sequence (WARNING) |
privilege |
CRITICAL | setuid, chmod 4777, sudo, PID 1 को signal भेजना |
cryptomining |
CRITICAL | stratum+tcp, xmrig, cryptonight |
runtime_install |
CRITICAL | pip install, npm install, curl ... \| sh |
dynamic_import |
CRITICAL | __import__(), importlib.import_module |
sandbox_escape |
CRITICAL | vm.runIn*, process.binding, new Function, __subclasses__(), __mro__ |
deserialization |
CRITICAL / WARNING | pickle.loads, marshal.loads, __reduce__; WARNING: Loader के बिना yaml.load() |
native_code |
CRITICAL / WARNING | ctypes.CDLL, ffi.Library; WARNING: Go unsafe |
compiled_binary |
WARNING, फिर BLOCK | .so, .dll, .exe, .wasm, .jar |
embedded_archive |
WARNING, फिर BLOCK | .zip, .tar.gz |
mime_mismatch |
CRITICAL माना जाता है | ऐसा extension जो तस्वीर होने का दावा करे, पर अंदर कुछ और हो |
suspicious_url |
WARNING | URL में HTTP के अलावा कोई scheme |
unvendored_deps |
WARNING | साथ पैक किए modules के बिना requirements.txt या package.json |
कोई CRITICAL बात या mime_mismatch इंस्टॉल मना करवा देती है। compiled_binary और
embedded_archive इसे मना करवाते हैं, जब तक manifest native-binary न बताए।
विश्लेषण क्या पक्का नहीं कर सकता
यह पैटर्न पर काम करता है। यह छिपाने के जाने-पहचाने तरीके खोल देता है (कई कदमों
में string जोड़कर कुछ कॉल करने लायक बनाना, नाम से builtin ढूँढना,
Function.constructor से बच निकलना), हर एक के लिए
tests/fixtures/plugins/malicious/ में एक regression fixture के साथ, पर यह चलते
समय होने वाले हर बदलाव के पीछे नहीं जा सकता (strrev("metsys"), किसी फ़ाइल से पढ़ा
payload decode करना)। विश्लेषण से पहले comment हटा दिए जाते हैं। सुरक्षा चलते समय का सैंडबॉक्स लागू
करता है, विश्लेषण नहीं: जो प्लगइन किसी पैटर्न को विश्लेषण से बचाकर निकाल ले, वह फिर भी
सर्वर का डेटा नहीं पढ़ सकता, मना किया सिस्टम कॉल नहीं कर सकता, अपनी संसाधन सीमाएँ
पार नहीं कर सकता, या ऐसा होस्ट method कॉल नहीं कर सकता जिसकी उसके पास अनुमति
नहीं।
सिग्नेचर¶
पैकेज पर cosign से सिग्नेचर किया जा सकता है (cosign sign-blob --key, ZIP के SHA-256
पर ECDSA P-256)। बनाने वाले की public key पैकेज के अंदर cosign.pub नाम से चलती है;
Evey उसी से अलग .sig को जाँचता है, अपने ही प्रोसेस में और offline
(pluginrunner/cosign.go), और कुंजी का fingerprint निकालता है (DER public key पर
sha256:)। इससे तीन बातें निकलती हैं:
- भरोसा: fingerprint इंस्टेंस की भरोसेमंद प्रकाशक सूची में है या नहीं
(
एडमिन → प्लगइन, या शुरू होते समयEVEY_PLUGINS_COSIGN_PUBLIC_KEYसे भरी हुई)। भरोसेमंद कुंजी प्रकाशक का नाम दिखाती है; अनजानी कुंजी अपना fingerprint और इस कुंजी पर भरोसा करें कार्रवाई दिखाती है। - निरंतरता: fingerprint इंस्टॉल के समय प्लगइन पर पिन हो जाता है। किसी और
कुंजी से साइन किया (
publisher-changed) या अब साइन न किया हुआ (signature-lost) अपडेट, नीति चाहे जो हो, तब तक मना रहता है जब तक एडमिन अपडेट डायलॉग में नया प्रकाशक स्वीकार नहीं करता (accept_new_publisher)। - नीति:
EVEY_PLUGINS_COSIGN_POLICYतय करती है कि फ़ैसले का क्या असर हो।
| नीति | असर |
|---|---|
off |
सिग्नेचर नहीं जाँचा जाता और कुछ दर्ज नहीं होता। |
warn (डिफ़ॉल्ट) |
फ़ैसला दर्ज होता है और दिखाया जाता है; बिना सिग्नेचर, भरोसे से बाहर और अमान्य पैकेज इंस्टॉल हो जाते हैं। |
enforce |
सिर्फ़ भरोसेमंद प्रकाशक का साइन किया पैकेज इंस्टॉल या अपडेट होता है। |
सिग्नेचर यह साबित करता है कि पैकेज वही है जो उसके प्रकाशक ने जारी किया, और कि अपडेट उसी प्रकाशक से आया है। कोड के बारे में यह कुछ नहीं कहता; उसे विश्लेषण, अनुमतियाँ और sandbox परखते हैं।
एडमिन का पूर्वावलोकन¶
POST /api/v1/admin/plugins/preview बिना इंस्टॉल किए विश्लेषण चलाता है। एडमिन माँगी
गई अनुमतियाँ, हर मिली बात उसकी फ़ाइल, लाइन और श्रेणी के साथ, कोड में मिले URL, और
पैकेज का SHA256 देखता है; एडमिन ने जिस hash को स्वीकार किया, वह अपडेट के समय जाँचा जाता है, और पहली बार का इंस्टॉल URL में #sha256: fragment से पक्का किया जा सकता है, इसलिए जिस
पैकेज का विश्लेषण हुआ, वही इंस्टॉल होता है। अपडेट अनुमतियों, स्लॉट और होस्ट का फ़र्क
दिखाता है, और नई अनुमति के लिए साफ़ स्वीकृति चाहिए।
ऊँची अनुमतियों के लिए भरोसे का निर्णय एडमिन का है: Evey दिखाता है कि प्लगइन किन चीज़ों तक पहुँचेगा, एडमिन स्वीकार या मना करता है।
चलते समय¶
छह परतें हर सैंडबॉक्स मोड में लागू रहती हैं; पहली दो कैसे लागू होती हैं, यह मोड पर निर्भर है।
फ़ाइल सिस्टम¶
प्लगइन अपना फ़ोल्डर और अपने socket का फ़ोल्डर देखता है, सर्वर का डेटा नहीं; यह एक
Landlock अनुमति सूची (landlock) या bubblewrap की mount अनुमति सूची (bubblewrap,
strict) से होता है। Landlock के बिना वाले kernel पर basic मोड में हर रास्ते के
हिसाब से कोई बंदिश नहीं होती: तब अलग उपयोगकर्ता और फ़ाइल की अनुमतियाँ ही सर्वर के
secret की रक्षा करती हैं।
नेटवर्क¶
प्लगइन के पास सीधा नेटवर्क नहीं होता, वह कुछ भी बताए: हर IP socket मना होता है
(landlock और basic में -deny-inet, bubblewrap और strict में एक network
namespace)। network अनुमति वाला प्लगइन अपने Unix socket पर होस्ट का Fetch method
कॉल करता है। होस्ट, internal/adapters/driving/pluginhost/fetch.go में:
- लक्ष्य को प्लगइन की
external_fetchसूची से मिलाता है (पूरे होस्ट नाम, बड़े-छोटे अक्षर का फ़र्क किए बिना;*.example.comwildcard इंस्टॉल पर मना हो जाता है; port का suffix अनदेखा किया जाता है); - सिर्फ़
httpऔरhttps, और सिर्फ़GETऔरHEADकी अनुमति देता है; - एक SSRF से सुरक्षित client (
internal/pkg/ssrf) से जुड़ता है, जो DNS से पता निकालने के बाद निजी, आरक्षित और link-local पते मना करता है, इसलिए DNS rebinding और redirect फिर से जाँचे जाते हैं (सूची के बाहर के होस्ट पर redirect मना है); - सर्वर का कोई credential नहीं जोड़ता, जवाब की body 8 MiB और request के header 8 KiB तक सीमित रखता है, और 20 s के बाद अनुरोध रद्द कर देता है।
इससे वे रास्ते बंद होते हैं जो सीधे बाहर जाने पर खुले रहते (169.254.169.254 जैसे IP,
प्लगइन का अपना resolver)। जो बचता है: broker यह तय करता है कि कॉल कहाँ जाती है, यह
नहीं कि उसमें क्या जाता है। देखें जानी-पहचानी सीमाएँ।
सर्वर चलाने वालों के लिए सुझाव
broker SSRF का ख़तरा होस्ट पर ले आता है। evey-server container पर बाहर जाने का
एक नियम, जो कोड से अलग हो, सुरक्षा की दूसरी परत जोड़ता है: 169.254.0.0/16 बंद करें और
अंदरूनी नेटवर्क को डेटाबेस और खोज के endpoint तक सीमित करें।
सिस्टम कॉल¶
दो seccomp फ़िल्टर उन्हीं ख़तरनाक सिस्टम कॉल को मना करते हैं:
- kernel फ़िल्टर, जिसे
evey-plugin-launcherप्लगइन शुरू करने से ठीक पहले लगाता है (seccomp_linux.go)। यह प्लगइन और उसके children को विरासत में मिलता है और compiled, static binary पर भी लागू होता है। किसी सिस्टम कॉल में गलत architecture हो, तो प्रोसेस समाप्त कर दिया जाता है।landlockऔरbasicमें लागू। LD_PRELOADफ़िल्टर,sandbox/sandbox.c, जो उन interpreter वाले प्लगइन में लोड होता है जोsubprocessनहीं बताते। यहexecve/execveatभी मना करता है, और प्लगइन को non-dumpable बनाता है।landlock,basicऔरstrictमें लागू। अगर library नहीं मिलती, तो ऐसे प्लगइन को शुरू होने से मना कर दिया जाता है (plugin.error)।
मना किए गए सिस्टम कॉल:
| श्रेणी | सिस्टम कॉल |
|---|---|
| Debugging | ptrace, process_vm_readv, process_vm_writev |
| Signal | tgkill, rt_sigqueueinfo, rt_tgsigqueueinfo, प्लगइन को छोड़कर किसी भी प्रोसेस की ओर (दोनों फ़िल्टर); kill भी इसी तरह kernel फ़िल्टर में, और LD_PRELOAD फ़िल्टर में किसी भी लक्ष्य की ओर; tkill kernel फ़िल्टर में (LD_PRELOAD फ़िल्टर इसे प्लगइन की खुद की ओर जाने देता है); pidfd_send_signal, pidfd_getfd (file descriptor चुराता है)। जो runtime कोई signal फिर से भेजता है या अपने ही thread को रोकता है, वह काम करता रहता है। Landlock ABI 6+ पर प्लगइन अपने सैंडबॉक्स के बाहर किसी प्रोसेस को signal भी नहीं भेज सकता |
| Mount | mount, umount2, pivot_root, chroot |
| Namespace | unshare, setns, और CLONE_NEW* flag वाला clone (दूसरे नाम से unshare), हर प्लगइन के लिए |
| Kernel module | init_module, finit_module, delete_module |
| BPF, perf | bpf, perf_event_open |
| हार्डवेयर I/O | ioperm, iopl (सिर्फ़ x86-64, जहाँ ये होते हैं) |
| Swap | swapon, swapoff |
| सिस्टम नियंत्रण | syslog, reboot, settimeofday, clock_settime, clock_adjtime, adjtimex |
| Kexec | kexec_load, kexec_file_load |
| kernel exploit के औज़ार | io_uring_setup, io_uring_enter, io_uring_register, userfaultfd |
| पहचान | setuid, setgid, setreuid, setregid, setresuid, setresgid, capset |
| Keyring | add_key, request_key, keyctl |
subprocess के बिना वाले प्लगइन के लिए kernel प्रोसेस बनाना भी मना करता है (fork,
vfork, CLONE_THREAD के बिना clone; clone3 ENOSYS लौटाता है ताकि C library
clone पर लौट आए); thread काम करते रहते हैं।
यह एक मना-सूची है: open, read, write, mmap, Unix socket और connect की
अनुमति बनी रहती है। रास्तों को फ़ाइल सिस्टम वाली परत सीमित करती है, seccomp नहीं।
bubblewrap में launcher इस्तेमाल नहीं होता: वहाँ static binary को कोई seccomp फ़िल्टर
नहीं मिलता, और उसे namespace और mount अनुमति सूची सीमित रखते हैं।
एक अलग उपयोगकर्ता¶
प्लगइन उपयोगकर्ता 1001 (evey-plugin) के रूप में चल सकता है, जो सर्वर (1000) से अलग
है, ताकि सर्वर के secret सिर्फ़ फ़ाइल की अनुमतियों से ही उसके लिए न पढ़े जा सकें।
EVEY_PLUGINS_UID_SEPARATION इसे नियंत्रित करता है:
| मान | बदलाव कब होता है |
|---|---|
auto (डिफ़ॉल्ट) |
सिर्फ़ तब जब सैंडबॉक्स basic पर पहुँचता है, वह अकेली स्थिति जहाँ फ़ाइल की अनुमतियाँ ही secret की एकमात्र सुरक्षा हैं |
on |
हमेशा, एक अतिरिक्त परत के रूप में |
off |
कभी नहीं |
बदलाव launcher करता है, जिसके पास cap_setuid है; सर्वर खुद बिना अधिकारों या
capabilities के चलता है। launcher 1000 से नीचे का कोई लक्ष्य मना करता है। अगर वह बदल
नहीं पाता, तो basic में प्लगइन मना होता है (वहाँ अलग उपयोगकर्ता ही एकमात्र सुरक्षा है), जबकि
landlock में वह एक चेतावनी के साथ सर्वर के उपयोगकर्ता के रूप में चलता है।
bubblewrap और strict में यह काम user namespace करता है।
चालू होने पर, साइन करने वाली कुंजी (0600) और बैकअप (0700 फ़ोल्डर, 0600 फ़ाइलें)
चलते समय सिर्फ़ मालिक के लिए होते हैं, जो bind-mount किए गए /data पर भी बना रहता है।
सभी प्लगइन उपयोगकर्ता 1001 साझा करते हैं: उन्हें एक-दूसरे से फ़ाइल सिस्टम वाली परत
अलग रखती है, उपयोगकर्ता नहीं।
होस्ट socket पर पहचान¶
हर प्लगइन का अपना socket होता है, /tmp/evey/plugins/<plugin_id>/host.sock, जिसे उसका
अपना gRPC सर्वर देता है। हर कॉल पर सर्वर kernel से कॉल करने वाले का PID पढ़ता है
(SO_PEERCRED, जिसे user space नकली नहीं बना सकता) और उसे प्लगइन शुरू होते समय दर्ज
किए PID से मिलाता है। प्लगइन की पहचान socket से आती है, कभी किसी header से नहीं।
इसलिए प्लगइन किसी दूसरे की पहचान नहीं ले सकता, किसी दूसरे प्लगइन के socket को कॉल नहीं
कर सकता, या किसी child process से कॉल नहीं भेज सकता। PID न मिलने पर एक चेतावनी लॉग
होती है।
संसाधनों की सीमाएँ¶
user space में एक निगरानी प्रक्रिया (बिना अधिकार वाले container में cgroups उपलब्ध
नहीं होते) हर 2 सेकंड में /proc/<pid> पढ़ता है और सीमा पार करने वाले प्लगइन को रोक
देता है (ADR-0013):
| सीमा | डिफ़ॉल्ट | कब जाँची जाती है |
|---|---|---|
| मेमोरी (RSS) | 256 MiB | हर दौर में |
| प्रोसेस और thread | 64 | हर दौर में (सिर्फ़ मुख्य प्रोसेस के thread की गिनती) |
| उसके फ़ोल्डर की डिस्क | 100 MiB | हर 15वें दौर में, लगभग 30 s |
| CPU प्राथमिकता | nice 10, शुरू होते समय लागू |
- |
वही दौर उस प्लगइन को चिह्नित करता है जो लगभग 30 s तक एक CPU का 90% से ज़्यादा उपयोग
करता रहता है, और hardware.gpu वाले उस प्लगइन को रोक देता है जो किसी ग्राफ़िक्स इंजन
का इसी तरह उपयोग करता रहता है। संसाधन सीमाओं का एक ही सेट, जो एडमिन → प्लगइन → संसाधन सीमाएँ में तय होता
है, हर प्लगइन पर लागू होता है; environment वेरिएबल EVEY_PLUGINS_MEMORY_MB, EVEY_PLUGINS_PIDS_MAX,
EVEY_PLUGINS_DISK_MAX_MB और EVEY_PLUGINS_CPU_NICE शुरुआती डिफ़ॉल्ट तय करते हैं।
इंटरफ़ेस का अलगाव¶
प्लगइन का इंटरफ़ेस एक <iframe sandbox="allow-scripts"> में चलता है, बिना same-origin
पहुँच, फ़ॉर्म या popup के, एक ऐसी Content-Security-Policy के तहत जो सिर्फ़ उसके
external_fetch होस्ट से कनेक्शन की अनुमति देती है। bridge
(frontend/packages/client/src/plugins/hostProtocol.ts) सीमित करता है कि वह पेज से
क्या माँग सकता है: toast एक सेकंड में ज़्यादा से ज़्यादा 3, 200 अक्षर, कोई HTML नहीं;
navigate सिर्फ़ /home, /library, /media, /player, /settings और /search
मानता है; resize 50 px (मोबाइल और टीवी पर 60) और 800 px के बीच। स्लॉट इंस्टॉल के
समय जाँचे जाते हैं।
सर्वर को मज़बूत बनाना¶
शुरू होते समय सर्वर खुद को non-dumpable बनाता है (PR_SET_DUMPABLE=0,
hardening_linux.go): उसी उपयोगकर्ता के रूप में चलने
वाला कोई प्रोसेस /proc से उसकी मेमोरी या environment नहीं पढ़ सकता, जिससे मेमोरी में
रखी साइन करने वाली कुंजी और डेटाबेस और खोज के पासवर्ड सुरक्षित रहते हैं, Landlock के
बिना वाले kernel पर भी।
क्रैश की सूचना¶
runner एक plugin.error event प्रकाशित करता है जब किसी प्लगइन को सैंडबॉक्स library
चाहिए पर वह न मिले, या जब कोई प्लगइन त्रुटि के साथ बंद हो। सूचना सर्विस इसे सेट किए
गए चैनलों से एडमिन तक भेजती है, और सुरक्षा डैशबोर्ड इसे दर्ज करता है।
सैंडबॉक्स मोड¶
मोड चुनता है कि फ़ाइल सिस्टम और नेटवर्क कैसे सीमित हों। EVEY_PLUGINS_SANDBOX_MODE
शुरुआती मान तय करता है (डिफ़ॉल्ट auto); इसे एडमिन → प्लगइन → संसाधन सीमाएँ →
सैंडबॉक्स मोड में बदला जा सकता है। runner इसे
applySandbox में लागू करता
है। हर मोड में: कोई सीधा IP निकास नहीं, संसाधनों की निगरानी, और होस्ट socket पर पहचान।
| परत | landlock | basic | bubblewrap | strict |
|---|---|---|---|---|
| फ़ाइल सिस्टम | Landlock अनुमति सूची | Landlock के बिना कुछ नहीं (सिर्फ़ फ़ाइल की अनुमतियाँ) | mount अनुमति सूची | mount अनुमति सूची |
| नेटवर्क | -restrict-net + -deny-inet |
-deny-inet |
network namespace | network namespace |
| kernel seccomp (static binary भी) | हाँ | हाँ | नहीं | नहीं |
LD_PRELOAD seccomp (subprocess के बिना interpreter वाले प्लगइन) |
हाँ | हाँ | नहीं | हाँ |
| अलग उपयोगकर्ता | on के साथ |
हाँ (auto) |
user namespace | user namespace |
| संसाधनों की सीमाएँ | हाँ | हाँ | हाँ | हाँ |
auto (डिफ़ॉल्ट)¶
पहली बार कोई प्लगइन शुरू होने पर, एक बार, उस सबसे मज़बूत मोड पर पहुँचता है जो होस्ट
सपोर्ट करता है: landlock, जब kernel Landlock लागू करता है और
evey-plugin-launcher binary मिल जाती है (सर्वर binary के बगल में, $PATH पर, या
EVEY_PLUGIN_LAUNCHER पर); वरना bubblewrap, जब एक टेस्ट सैंडबॉक्स सच में बन जाता
है (ProbeSandbox), जिसका
व्यवहार में मतलब है bare metal या अधिकार वाला container; वरना basic। Landlock पहले
आता है क्योंकि उसे किसी अधिकार की ज़रूरत नहीं और वह Docker की डिफ़ॉल्ट सुरक्षा प्रोफ़ाइल
में चलता है। चुना गया मोड लॉग होता है, और एडमिन → प्लगइन → सुरक्षा डैशबोर्ड की
होस्ट सैंडबॉक्स लाइन पर दिखता है।
| डिप्लॉयमेंट | auto क्या देता है |
|---|---|
| नए kernel पर Docker | landlock: हर रास्ते के हिसाब से बंदिश, कोई सीधा निकास नहीं, ख़तरनाक सिस्टम कॉल मना, static binary समेत। सामान्य स्थिति। |
| Landlock के बिना पुराने kernel पर Docker, जैसे कोई पुराना NAS | basic: secret सुरक्षित और नेटवर्क कटा हुआ, पर सबके पढ़ने लायक फ़ाइलें दिखती हैं। देखें प्लगइन सैंडबॉक्स सेटअप। |
| Bare metal | bubblewrap: फ़ाइल सिस्टम और प्रोसेस का सबसे मज़बूत अलगाव, static binary के लिए seccomp फ़िल्टर के बिना। |
landlock¶
- फ़ाइल सिस्टम: launcher की लगाई Landlock अनुमति सूची। सिस्टम फ़ोल्डर (
/usr,/lib,/lib64,/bin,/sbin,/etc,/app) सिर्फ़ पढ़ने के लिए;/devमें से सिर्फ़null,zero,full,randomऔरurandom, ताकि ग्राफ़िक्स कार्ड के node और सर्वर का terminal पहुँच से बाहर रहें; प्लगइन का फ़ोल्डर और socket का फ़ोल्डर पढ़ने और लिखने के लिए; बाकी सब,/dataऔर दूसरे प्लगइन समेत, मना।hardware.gpuवाले प्लगइन को अकेले अपवाद के रूप में अपना render node मिलता है। - नेटवर्क:
-restrict-netऔर-deny-inet; बाहर सिर्फ़Fetchसे। - सिस्टम कॉल: kernel फ़िल्टर और
LD_PRELOADफ़िल्टर। - ज़रूरत: kernel 5.13 या नया, जिसकी चालू LSM सूची में Landlock हो, और launcher (नए
होस्ट पर आधिकारिक इमेज में दोनों होते हैं)। साफ़ तौर पर चुना गया
landlockऐसा प्लगइन शुरू करने से मना करता है जिसे वह सीमित न कर सके।
bubblewrap¶
- फ़ाइल सिस्टम और प्रोसेस: छह अलग namespace (
user,pid,uts,ipc,cgroup,net) और एक mount अनुमति सूची; अंदर और कुछ मौजूद नहीं होता (pluginrunner/runner_bwrap.goमेंbuildBwrapCmd)।
| रास्ता | पहुँच |
|---|---|
/usr, /bin, /sbin, /lib, /lib64 |
सिर्फ़ पढ़ना |
/etc/ssl, /etc/ca-certificates, /etc/resolv.conf, /etc/nsswitch.conf, /etc/{alpine,os}-release |
सिर्फ़ पढ़ना (पूरा /etc कभी नहीं: न passwd, न shadow, न ssh) |
| प्लगइन का फ़ोल्डर, उसके socket का फ़ोल्डर | पढ़ना और लिखना |
/proc |
उसका अपना mount |
/dev |
एक छोटा बनाया हुआ समूह (null, zero, random, urandom, tty, pts) |
/tmp |
एक खाली tmpfs |
/data, /var, /home, /root, /run और दूसरे प्लगइन के फ़ोल्डर अंदर मौजूद ही नहीं
होते। एक canary प्लगइन (tests/fixtures/plugins/canary-sandbox-runtime/) चलते स्टैक
पर इस बनावट की जाँच करता है।
- नेटवर्क: network namespace, हर प्लगइन के लिए।
- सिस्टम कॉल: कोई seccomp फ़िल्टर नहीं; अलगाव namespace देते हैं।
- ज़रूरत: bwrap binary और mount/unshare सिस्टम कॉल। Docker में इसके लिए ढीली
सुरक्षा प्रोफ़ाइल चाहिए, इसलिए व्यवहार में यह bare metal वाला मोड है।
strict¶
bubblewrap और साथ में LD_PRELOAD फ़िल्टर (सिर्फ़ interpreter वाले प्लगइन)।
basic¶
auto का आख़िरी विकल्प: अलग उपयोगकर्ता, kernel फ़िल्टर, LD_PRELOAD फ़िल्टर,
-deny-inet, और Landlock अगर kernel में हो। Landlock के बिना वाले kernel पर हर रास्ते
के हिसाब से कोई बंदिश नहीं होती: प्लगइन सबके पढ़ने लायक गैर-संवेदनशील फ़ाइलें पढ़ सकता
है और प्रोसेस की सूची देख सकता है। सर्वर के secret अलग उपयोगकर्ता और
PR_SET_DUMPABLE से सुरक्षित रहते हैं, नेटवर्क कटा रहता है, और ख़तरनाक सिस्टम कॉल मना
रहते हैं, compiled binary समेत।
कॉन्फ़िगरेशन¶
| वेरिएबल | डिफ़ॉल्ट | असर |
|---|---|---|
EVEY_PLUGINS_SANDBOX_MODE |
auto |
auto, landlock, bubblewrap, strict या basic। एडमिन → प्लगइन में भी बदला जा सकता है। |
EVEY_PLUGINS_UID_SEPARATION |
auto |
auto, on या off। container पर no_new_privs के साथ नहीं चलता। |
EVEY_PLUGINS_COSIGN_POLICY |
warn |
off, warn या enforce। |
EVEY_PLUGINS_COSIGN_PUBLIC_KEY |
कोई नहीं | वैकल्पिक: एक PEM public key, जो शुरू होते समय भरोसेमंद प्रकाशकों में जुड़ जाती है। |
EVEY_PLUGINS_MEMORY_MB, EVEY_PLUGINS_PIDS_MAX, EVEY_PLUGINS_DISK_MAX_MB, EVEY_PLUGINS_CPU_NICE |
256, 64, 100, 10 | संसाधन सीमाओं के शुरुआती डिफ़ॉल्ट; पैनल इन्हें सभी प्लगइन के लिए एक साथ बदल देता है। |
आधिकारिक इमेज में evey-plugin-launcher (Landlock और kernel फ़िल्टर) और bubblewrap
आते हैं। bubblewrap को होस्ट पर बिना अधिकार वाले user namespace भी चाहिए। कौन सा सेटअप
किस मशीन के लिए ठीक है, आसान शब्दों में:
प्लगइन सैंडबॉक्स सेटअप।
जानी-पहचानी सीमाएँ¶
| सीमा | असर | आज इसे क्या सीमित करता है |
|---|---|---|
Landlock के बिना वाले kernel पर basic में हर रास्ते के हिसाब से कोई बंदिश नहीं |
प्लगइन सबके पढ़ने लायक गैर-संवेदनशील फ़ाइलें और दूसरे प्लगइन के फ़ोल्डर पढ़ सकता है | secret अलग उपयोगकर्ता और PR_SET_DUMPABLE से सुरक्षित; auto Landlock या bubblewrap को पहले चुनता है |
| सभी प्लगइन उपयोगकर्ता 1001 साझा करते हैं | basic मोड में प्लगइन अपने साथियों की फ़ाइलें पढ़ सकता है |
दूसरे प्रोसेस को signal भेजना बंद है (seccomp, LD_PRELOAD और bubblewrap तीनों इसे रोकते हैं); PR_SET_DUMPABLE /proc से दूसरे प्रोसेस पढ़ना रोकता है; Landlock और bubblewrap प्लगइन के फ़ोल्डर अलग रखते हैं |
| Seccomp एक मना-सूची है | फ़ाइल और Unix socket वाली कॉल की अनुमति रहती है | रास्ते Landlock या bubblewrap सीमित करते हैं; IP socket मना हैं |
| broker SSRF का ख़तरा होस्ट पर ले आता है | broker की जाँच में कोई कमी किसी अंदरूनी पते तक पहुँच सकती है | Go में अनुमति सूची और SSRF से सुरक्षित client; container पर बाहर जाने का नियम, एक अलग परत के रूप में |
| broker तय करता है कि कॉल कहाँ जाती है, यह नहीं कि उसमें क्या जाता है | डेटा प्लगइन की सूची के किसी होस्ट की ओर बाहर जा सकता है | सिर्फ़ उन होस्ट की अनुमति दें जिन पर आप उस डेटा के साथ भरोसा करते हैं |
| विश्लेषण पैटर्न पर काम करता है | जानबूझकर छिपाया गया कोड बच सकता है | एडमिन का पूर्वावलोकन; सीमा सैंडबॉक्स है, विश्लेषण नहीं |
LD_PRELOAD सिर्फ़ dynamic, interpreter वाले प्लगइन पर लागू होता है |
launcher के बिना basic में एक static binary इससे बच जाती है |
आधिकारिक इमेज में launcher हमेशा आता है, जिसका kernel फ़िल्टर static binary पर लागू होता है |
नेटवर्क पर खुलापन (प्लगइन ख़तरा मॉडल से बाहर)¶
दो पक्के नियम तय करते हैं कि सर्वर तक कौन पहुँच सकता है:
- सिर्फ़ HTTP port ही खोलने के लिए है। gRPC port (
EVEY_SERVER_PORT, डिफ़ॉल्ट 9090) एक अंदरूनी रास्ता है, जिसका अकेला client उसी प्रोसेस का HTTP gateway है; HTTP API उस port से एक आगे वाला port है। gRPC डिफ़ॉल्ट रूप से loopback पर bind होता है (EVEY_GRPC_BIND=127.0.0.1), क्योंकि रिमोट एक्सेस की जाँच HTTP middleware है और इस पर लागू नहीं होती। bind का दायरा बढ़ाने पर एक चेतावनी लॉग होती है; तब उसे firewall से बंद करना सर्वर चलाने वाले का काम है। - रिमोट एक्सेस की जाँच उस एक client पते से निर्णय लेती है जो भरोसेमंद proxy की सीमा
पर जाँचा गया हो, कभी किसी request header से नहीं। कोई client
X-Forwarded-Forसे127.0.0.1होने का दावा नहीं कर सकता, और जो request उस सीमा से कभी नहीं गुज़रा, वह रिमोट माना जाता है। यह डिप्लॉयमेंट का नियंत्रण है, पहचान नहीं।
देखें रिमोट एक्सेस और HTTPS और कॉन्फ़िगरेशन रेफ़रेंस।
लॉग में क्या हो सकता है¶
सर्वर का लॉग लॉग और डीबग, bug रिपोर्ट और बैकअप में जाता है, इसलिए लाइन लिखते समय
credentials साफ़ कर दिए जाते हैं (internal/pkg/logger/redact.go): जिस attribute की
कुंजी किसी secret का नाम लेती है (authorization, token, api_key, password,
pin, secret...), वह पूरा बदल दिया जाता है, और bearer token, JWT या secret=value
जोड़ा रखने वाली हर string साफ़ की जाती है। लॉग वाले route पुरानी लाइनों पर भी बाहर
जाते समय यही नियम लागू करते हैं। कोड के लिए: किसी गैर-secret मान को secret वाले कुंजी
नाम के तहत कभी लॉग न करें (वह छिप जाएगा), और किसी नए तरह के credential को लॉग होने से
पहले ही उस सूची में जोड़ दें।
कमज़ोरी की रिपोर्ट करना¶
निजी तौर पर रिपोर्ट करें, किसी सार्वजनिक issue में कभी नहीं: security@eveyproject.org,
जो repository की सुरक्षा नीति का पता है। इसमें प्लगइन की पहचान,
अनुमतियाँ और सैंडबॉक्स से बच निकलना भी बाकी सब की तरह शामिल हैं। उन Evey सर्वरों पर
टेस्ट न करें जो आपके नहीं हैं।