
CVE-2022-25173 के लिए एक्सप्लॉइट जो जेनकिंस पाइपलाइन ग्रूवी प्लगइन के CPS इंटरप्रेटर सैंडबॉक्स बायपास को लक्षित करता है, जो जेनकिंस कंट्रोलर में मनमाना कोड निष्पादन को सक्षम बनाता है, जो तैयार किए गए पाइपलाइन स्क्रिप्ट के माध्यम से होता है।
पाइपलाइन प्लगइन सूट का एक प्रमुख घटक, यह पाइपलाइन चरणों के लिए मानक निष्पादन इंजन प्रदान करता है, जो जेनकिंस नियंत्रक प्रक्रिया के अंदर चलने वाले एक कस्टम ग्रूवी इंटरप्रेटर पर आधारित है।
(सिद्धांत रूप में अन्य निष्पादन इंजनों को भी समर्थित किया जा सकता है, जिसमें FlowDefinition API प्रवेश बिंदु है, लेकिन कोई भी प्रोटोटाइप नहीं किया गया है और इसे लिखना संभवतः एक बहुत बड़ा प्रयास होगा।)
पाइपलाइन ग्रूवी स्क्रिप्ट कोड जैसे
retry(3) {
for (int i = 0; i < 10; i++) {
branches["branch${i}"] = {
node {
retry(3) {
checkout scm
}
sh 'make world'
}
}
}
parallel branches
एक ग्रूवी प्रोग्राम के रूप में चलाया जाता है, जिसमें कुछ विशेष फ़ंक्शन कॉल होते हैं जिन्हें चरण कहा जाता है जो जेनकिंस-विशिष्ट संचालन करते हैं।
इस उदाहरण में चरण parallel इस प्लगइन में परिभाषित है, जबकि node, retry, checkout, और sh पाइपलाइन सूट के अन्य प्लगइन में परिभाषित हैं। scm वैश्विक चर पाइपलाइन मल्टीब्रांच प्लगइन में परिभाषित है।
ग्रूवी स्क्रिप्ट को WorkflowScript नामक एक वर्ग में संकलित किया जाता है, इसलिए स्टैक ट्रेसेस में स्क्रिप्ट फ़ाइल-नाम (जैसे Jenkinsfile) के बजाय यही नाम दिखाया जाता है।
एक नियमित ग्रूवी प्रोग्राम के विपरीत जो कमांड लाइन से चलाया जाता है, पाइपलाइन बिल्ड के प्रोग्राम की पूरी स्थिति हर बार डिस्क पर सहेजी जाती है जब एक अतुल्यकालिक ऑपरेशन किया जाता है, जिसमें अधिकांश पाइपलाइन चरण शामिल हैं।
जेनकिंस को एक बिल्ड चलने के दौरान पुनरारंभ किया जा सकता है, और प्रोग्राम को वहाँ से पुनः चलाना शुरू कर देगा जहाँ वह छोड़ा गया था।
यह कुशल होने के लिए नहीं है, और इसलिए इसे जेनकिंस सुविधाओं से सीधे संबंधित उच्च-स्तरीय "गोंद" कोड तक सीमित रखा जाना चाहिए;
आपकी परियोजना की अपनी बिल्ड लॉजिक को एक बिल्ड नोड पर बाहरी प्रोग्रामों से sh या bat चरण में चलाया जाना चाहिए।
JIRA में पाइपलाइन ग्रूवी महाकाव्य ग्रूवी इंटरप्रेटर की कुछ ज्ञात सीमाओं को कवर करता है। ये समस्याएँ इस तथ्य से उत्पन्न होती हैं कि पाइपलाइन ग्रूवी को सीधे नहीं चला सकता, बल्कि प्रोग्राम की स्थिति को सहेजने के लिए प्रत्येक ऑपरेशन को रोकना होता है।
पाइपलाइन सैंडबॉक्स महाकाव्य ग्रूवी सैंडबॉक्स से संबंधित मुद्दों को कवर करता है जिसका उपयोग दुर्भावनापूर्ण पाइपलाइन स्क्रिप्ट को जेनकिंस पर नियंत्रण लेने से रोकने के लिए किया जाता है। सैंडबॉक्स अक्षम होने पर चलाई गई स्क्रिप्ट जेनकिंस आंतरिक API को सीधे कॉल कर सकती हैं, जो लापता चरण कार्यक्षमता के लिए एक उपयोगी समाधान हो सकता है, लेकिन सुरक्षा कारणों से केवल प्रशासक ही ऐसी स्क्रिप्ट को अनुमोदित कर सकते हैं।
पाइपलाइन स्निपेट जनरेटर महाकाव्य लाइव कॉन्फ़िगरेशन फ़ॉर्म पर चरण सिंटैक्स के नमूने प्रदान करने के लिए उपयोग किए जाने वाले उपकरण से संबंधित मुद्दों को कवर करता है।
यह प्लगइन पहले "वर्कफ़्लो CPS प्लगइन" या "वर्कफ़्लो ग्रूवी प्लगइन" था। तदनुसार इसका Maven artifactId workflow-cps है, न कि pipeline-groovy।
प्लगइन प्रोग्राम को संकलित करने पर कंटिन्यूएशन-पासिंग शैली रूपांतरण को लागू करने के लिए ग्रूवी CPS लाइब्रेरी का उपयोग करता है।
मानक ग्रूवी कंपाइलर का उपयोग AST बनाने के लिए किया जाता है, लेकिन बाइटकोड का निर्माण एक CompilationCustomizer द्वारा रोका जाता है जो अधिकांश ऑपरेशनों को एक विशेष "त्रुटि", CpsCallableInvocation फेंकने वाले वेरिएंट से बदल देता है।
इसे तब इंजन द्वारा पकड़ा जाता है, जो इससे प्राप्त जानकारी (जैसे किसी विधि कॉल पर पास किए जाने वाले तर्क) का उपयोग करके नियंत्रण को अगले कंटिन्यूएशन पर स्थानांतरित करता है।
पाइपलाइन स्क्रिप्ट एनोटेशन @NonCPS के साथ नामित विधियों को चिह्नित कर सकती हैं।
फिर इन्हें सामान्य रूप से संकलित किया जाता है (सैंडबॉक्स सुरक्षा जाँचों को छोड़कर), और इसलिए वे जावा प्लेटफ़ॉर्म, ग्रूवी रनटाइम, या जेनकिंस कोर या प्लगइन कोड से "बाइनरी" विधियों की तरह व्यवहार करती हैं।
@NonCPS विधियाँ सुरक्षित रूप से गैर-Serializable ऑब्जेक्ट को स्थानीय चर के रूप में उपयोग कर सकती हैं, हालाँकि उन्हें गैर-सीरियलाइज़ेबल पैरामीटर स्वीकार नहीं करने चाहिए या गैर-सीरियलाइज़ेबल मान वापस या संग्रहीत नहीं करने चाहिए।
आप @NonCPS विधि से नियमित (CPS-रूपांतरित) विधियाँ, या पाइपलाइन चरण, कॉल नहीं कर सकते हैं, इसलिए वे मुख्य स्क्रिप्ट में सारांश वापस करने से पहले कुछ गणना करने के लिए सबसे उपयुक्त हैं।
विशेष रूप से ध्यान दें कि बाइनरी वर्गों में परिभाषित विधियों के @Overrides,
जैसे Object.toString(),
को सामान्यतः @NonCPS चिह्नित किया जाना चाहिए क्योंकि उन्हें अक्सर बाइनरी कोड द्वारा कॉल किया जाएगा।
कुछ प्रकार की वस्तुएँ स्वाभाविक रूप से सीरियलाइज़ करने के लिए सुरक्षित नहीं होती हैं, फिर भी हम प्रोग्राम ग्राफ़ में उनका संदर्भ बनाए रखना चाहते हैं।
एक उदाहरण Executor (~ बिल्ट-इन या एजेंट नोड पर एक्ज़ीक्यूटर स्लॉट) है जो node चरण द्वारा अपने ब्लॉक में किसी भी चरण, विशेष रूप से sh/bat, को पारित किए गए संदर्भ का हिस्सा है।
पाइपलाइन इन वस्तुओं के सीरियलाइज़ेशन-सुरक्षित संस्करणों को प्रतिस्थापित करने के लिए Pickle API का उपयोग करती है।
जब एक WorkflowRun को पुनरारंभ के बाद डिस्क से लोड किया जाता है, तो प्रोग्राम स्थिति को डी-सीरियलाइज़ किया जाता है, और पिकल्स को समानांतर में डी-सीरियलाइज़ ("रीहाइड्रेट") किया जाता है।
यदि और जब सभी पिकल्स सफलतापूर्वक डी-सीरियलाइज़ हो जाते हैं और परिणामी वस्तुओं को प्रोग्राम स्थिति में वापस रख दिया जाता है, तो प्रोग्राम फिर से चलना शुरू हो जाता है, और टाइमर आदि को पुनर्स्थापित करने के लिए StepExecution.onResume कॉल किया जाता है।
सभी प्रोग्राम लॉजिक एक "CPS VM थ्रेड" के अंदर चलाए जाते हैं, जो कि केवल एक जावा थ्रेड पूल है जो बाइनरी विधियों को चला सकता है और यह पता लगा सकता है कि आगे कौन सा कंटिन्यूएशन करना है।
parallel चरण "ग्रीन थ्रेड" (जिसे सहकारी मल्टीटास्किंग भी कहा जाता है) का उपयोग करता है: यह विभिन्न क्रियाओं के लिए तार्किक थ्रेड (~ शाखा) नामों को रिकॉर्ड करता है, लेकिन उन्हें वस्तुतः एक साथ नहीं चलाता है।
प्रोग्राम एक साथ कार्य करता हुआ प्रतीत हो सकता है, लेकिन केवल इसलिए क्योंकि अधिकांश चरण अतुल्यकालिक रूप से चलते हैं, जबकि VM थ्रेड निष्क्रिय होता है, और वे समय में ओवरलैप हो सकते हैं।
कोई जावा थ्रेड केवल उन आमतौर पर संक्षिप्त अंतरालों के दौरान खपत होता है जब ग्रूवी कोड वास्तव में VM थ्रेड पर चलाया जा रहा होता है।
एक्ज़ीक्यूटर विजेट बिल्ट-इन नोड पर "फ्लाईवेट" एक्ज़ीक्यूटर के लिए केवल तब एक प्रविष्टि दिखाता है जब VM थ्रेड व्यस्त होता है; सामान्यतः यह छिपा रहता है।