বিষাক্ত-pipelineমৃত্যুদণ্ড-২

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (২): পরোক্ষ বিষক্রিয়া Pipeline বাস্তবায়ন (আই-পিপিই)

সুচিপত্র

অবশ্যই পঠনীয় পোস্ট

আমাদের আগের পোস্টে, আমরা দেখেছি কীভাবে সরাসরি বিষক্রিয়া শনাক্ত ও প্রতিরোধ করা যায়। Pipeline এক্সিকিউশন (ডি-পিপিই)। আমরা আরও দেখেছি কিভাবে ব্যবহার করে সেই দুর্বলতাটি শনাক্ত করা যায়। জাইজেনি স্ক্যানারপাশাপাশি কিছু সুরক্ষা ব্যবস্থাও। 

 বিষাক্ত Pipeline মৃত্যুদণ্ড (PPE) তৈরি হয় যখন আক্রমণকারী পরিবর্তন করতে পারে pipeline দুই ধরনের যুক্তির যেকোনো একটি:

  • CI কনফিগারেশন ফাইলটি পরিবর্তন করে ( pipeline) -> সরাসরি পিপিই (ডি-পিপিই)
  • উল্লেখিত ফাইলগুলি পরিবর্তন করে pipeline (উদাহরণস্বরূপ: এর ভেতর থেকে রেফারেন্স করা স্ক্রিপ্টগুলো) pipeline কনফিগারেশন ফাইল) -> পরোক্ষ PPE (I-PPE)
pp2

এই পোস্টে আমরা ইনডাইরেক্ট পিপিই (Indirect PPE) নিয়ে বিস্তারিত আলোচনা করব। কিন্তু তার আগে, এবং আমার আগের পোস্টের পরিপূরক হিসেবে, চলুন প্রথমে দেখে নেওয়া যাক গিটহাব (GitHub) কীভাবে এর এক্সিকিউশন পরিচালনা করে। pipelineএবং ডি-পিপিই থেকে সুরক্ষার ব্যবস্থাগুলো কী কী?

গিটহাব কীভাবে এক্সিকিউশন সুরক্ষিত করে pipelineপিআরদের কাছ থেকে আসছে?

পরিবর্তিত ফাইল কার্যকর করার ক্ষেত্রে গিটহাব কীভাবে কাজ করে? pipelines?

পরিমিত pipelines ধাক্কা বা থেকে আসতে পারে Pull Requests (জনসংযোগ) একটি প্রধান উত্তম অনুশীলন হিসেবে, কোনো সুরক্ষিত ব্রাঞ্চে সরাসরি “পুশ” করা থেকে বিরত থাকার এবং ব্যবহার করার জন্য দৃঢ়ভাবে সুপারিশ করা হয়। Pull Requests যেকোনো অবদানকৃত কোড গ্রহণ করার আগে কিছু পর্যালোচনা নিশ্চিত করার একটি প্রক্রিয়া হিসেবে। 

Pull Requests দুটি ভিন্ন উৎস থেকে আসতে পারে:

  • পিআর আসছে কাটাচামচ
  • পিআর আসছে শাখা

পিআর থেকে কাটাচামচ যেকোনো একটি থেকে আসতে পারে প্রকাশ্য or ব্যক্তিগত ভান্ডার।

যেহেতু আমরা PPE (বিষাক্ত) নিয়ে কাজ করছি Pipeline (বাস্তবায়ন), আমাদের মূল বিষয় কোনো পিআর (PR)-এর “গ্রহণ” নয়, বরং একটি পরিবর্তিত ফাইলের বাস্তবায়ন। pipeline পিআর (PR)-এর গ্রহণ/অনুমোদন প্রক্রিয়া চলাকালীন। একটি পিপিই (PPE) আক্রমণের মূলে থাকে একটি "ক্ষতিকর" পরিবর্তিত ফাইলের অনিচ্ছাকৃত নির্বাহ। pipeline. 

সংক্ষেপে, বিষাক্ত Pipeline কার্য সম্পাদন (PPE) তৈরি হয় যখন আক্রমণকারী পরিবর্তন করতে পারে pipeline যুক্তিবিদ্যা.

দুই আছে রূপগুলো:

  • সরাসরি পিপিই (ডি-পিপিই): একটি D-PPE পরিস্থিতিতে, আক্রমণকারী CI কনফিগারেশন ফাইলটি পরিবর্তন করে যে রিপোজিটরিতে তাদের অ্যাক্সেস আছে, সেখানে হয় সরাসরি রিপোটির একটি অসুরক্ষিত রিমোট ব্রাঞ্চে পরিবর্তনটি পুশ করে, অথবা কোনো ব্রাঞ্চ বা ফর্ক থেকে পরিবর্তনসহ একটি পিআর (PR) জমা দিয়ে পরিবর্তনটি করা যায়। যেহেতু সিআই pipeline পরিবর্তিত CI কনফিগারেশন ফাইলের কমান্ডগুলো দ্বারা এক্সিকিউশন নির্ধারিত হয়, আক্রমণকারীর ক্ষতিকারক কমান্ডগুলো অবশেষে বিল্ড নোডে রান করে। pipeline আলোড়ন সৃষ্টি হয়.
  • পরোক্ষ PPE (আই-পিপিইকিছু ক্ষেত্রে, কোনো প্রতিপক্ষের কাছে অ্যাক্সেস থাকা সত্ত্বেও ডি-পিপিই (D-PPE) এর সম্ভাবনা উপলব্ধ থাকে না। SCM সংগ্রহস্থল (যেমন যদি pipeline একই রিপোজিটরিতে থাকা একটি আলাদা, সুরক্ষিত শাখা থেকে CI কনফিগারেশন ফাইলটি পুল করার জন্য কনফিগার করা হয়েছে)। এমন পরিস্থিতিতে, বিষ প্রয়োগের পরিবর্তে pipeline আক্রমণকারী নিজেই রেফারেন্সকৃত ফাইলগুলিতে ক্ষতিকারক কোড প্রবেশ করায়। pipeline (উদাহরণস্বরূপ: এর ভেতর থেকে রেফারেন্স করা স্ক্রিপ্টগুলো) pipeline কনফিগারেশন ফাইল)

উভয় ক্ষেত্রেই, গিটহাব পরিবর্তিত সংস্করণটি কার্যকর করবে pipeline পূর্ববর্তী পর্যালোচনা বা অনুমোদনের কোনো প্রয়োজন ছাড়াই.

ফর্ক থেকে পিআর প্রকাশ্য বিশ্রাম

গিটহাব প্রসেসিং করার সময় আচরণ কনফিগার করার সুযোগ দেয়। পাবলিক রিপোজিটরিতে থাকা ফর্কগুলো থেকে আসা পিআরগুলো.

যখন কোনো PR একটি ফর্ক থেকে আসে, GitHub সেটি কার্যকর করার আগে সর্বদা এক ধরনের “অনুমোদন” বাধ্যতামূলক করে। pipeline জনসংযোগের সাথে যুক্তঅনুমোদনের এই স্তরটি দুর্বল থেকে কঠোর অনুমোদনের মধ্যে একটি ভারসাম্য রক্ষা করে।

At সংস্থা স্তর (Org>>Settings>>Actions>>General), আপনি বেশ কয়েকটি “অনুমোদন” বিকল্পের মধ্যে থেকে সিদ্ধান্ত নিতে পারেন:

পিপিই৮

সবচেয়ে কঠোর হলো শেষেরটি (“সকল বহিরাগত সহযোগীর অনুমোদন প্রয়োজন।”) কারণ বাইরের সহযোগীদের ফর্ক থেকে পিআর (PR) এলে গিটহাব সর্বদা অনুমোদনের প্রয়োজন মনে করবে। 

কিন্তু এই কঠোর ক্ষেত্রেও আছে পঠন এবং লেখার অনুমতি সহ সহযোগীদের মধ্যে পার্থক্য.

  • যখন জনসংযোগ একটি থেকে আসে পড়া ব্যবহারকারী, কার্যকর করা pipeline বন্ধ করা হয়েছে যতক্ষণ না পরিবর্তনগুলো অনুমোদিত হয়। অনুমোদন ঠিক থাকলে, সংশোধিত সংস্করণটি কার্যকর হবে। pipeline মৃত্যুদন্ড কার্যকর করা হয় 
  • যখন জনসংযোগ একটি থেকে আসে লেখা ব্যবহারকারী, অনুমোদনের প্রয়োজন নেই এবং সংশোধিত pipeline সর্বদা কার্যকর করা হয়!! 
pp4

উপসংহারে বলা যায়, পাবলিক রিপোজিটরির ফর্ক থেকে আসা পিআরগুলো পিপিই-এর বিরুদ্ধে সামান্যই সুরক্ষিত। বহিরাগত (রিড) ব্যবহারকারীদের জন্য কিছু সুরক্ষা থাকলেও, অভ্যন্তরীণ (রাইট) ব্যবহারকারীদের জন্য কোনো সুরক্ষা নেই।

কি সম্পর্কে ব্যক্তিগত রিপো থেকে ফর্ক করে আসা পিআর?

ফর্ক থেকে পিআর ব্যক্তিগত বিশ্রাম

এই পরিস্থিতিতে, গিটহাব কিছু দরকারি কনফিগারেশন সেটিংস প্রদান করে।

পিপিই৮

উপরের সেটিংসগুলি এখানে কনফিগার করা যেতে পারে অর্গ বা এ রেপো স্তর।

কখন কোনো বিকল্পই নির্বাচন করা হয়নি, গিটহাব করবে অনুমোদনের জন্য জিজ্ঞাসা করুন এবং এটি পরিবর্তিতটি কার্যকর করবে না pipelineএটাই সবচেয়ে নিরাপদ বিন্যাস!!

সার্জারির সবচেয়ে অনিরাপদ কনফিগারেশন কখন "ফর্ক থেকে ওয়ার্কফ্লো চালান pull request" যাচাই করা হয়েছেএক্ষেত্রে, রিড এবং রাইট উভয় ব্যবহারকারীর জন্যই, গিটহাব স্বয়ংক্রিয়ভাবে পরিবর্তিত ফাইলটি কার্যকর করবে। pipelineএবং এই পরিস্থিতি আরও হতে পারে খারাপ যদি “ফর্ক থেকে ওয়ার্কফ্লোতে রাইট টোকেন পাঠান pull requests" এবং "ফর্ক থেকে ওয়ার্কফ্লোতে গোপনীয় তথ্য এবং ভেরিয়েবল পাঠান pull requests” যাচাই করা হয়। সুস্পষ্ট কারণ ছাড়া এটি করবেন না!!

যদি "ফর্কের জন্য অনুমোদন প্রয়োজন pull request কর্মপ্রবাহ"চেক করা হলে, উপরের পরিস্থিতিটি কিছুটা উন্নত হয়: GitHub অনুমোদনের জন্য জিজ্ঞাসা করবে এবং পরিবর্তিত ফাইলটি কার্যকর করবে না।" pipeline রিড ইউজারের জন্য নয়, তবে এটি রাইট ইউজারের জন্যও কার্যকর হবে।

পিপিই৮

কাঁটাচামচ দেখা গেছে, তাহলে কী হবে? শাখা থেকে আসা পিআর?

পিআর থেকে শাখা

এই পরিস্থিতি রক্ষা করতে আপনাকে নির্ভর করতে হবে শাখা সুরক্ষা নিয়মাবলী

রিপো লেভেলে, আপনি যেকোনো ব্রাঞ্চের জন্য ব্রাঞ্চ প্রোটেকশন রুল তৈরি করতে পারেন। এই রুলগুলো কিছু অতিরিক্ত সুবিধা যোগ করে। সুরক্ষিত শাখাগুলির পরিবর্তনের উপর সীমাবদ্ধতা.

যদিও আপনি একটি নিয়ম কনফিগার করেন “প্রয়োজন একটি pull request একীভূত করার আগে" এবং "অনুমোদন প্রয়োজন" পরিবর্তিত pipeline পিআর তৈরি করার সাথে সাথে এটি স্বয়ংক্রিয়ভাবে কার্যকর হবে।এই “অনুমোদন” শুধুমাত্র একত্রীকরণ কার্যক্রমের ক্ষেত্রেই প্রযোজ্য হবে।

পিপিই৮

পরোক্ষ বিষক্রিয়া সম্পর্কে কী বলা যায়? Pipeline ফাঁসি

যেমনটি আমরা উপরে দেখেছি, ডি-পিপিই ব্যবহার করে প্রশমিত করা যেতে পারে। পুল_রিকোয়েস্ট_টার্গেট, কিন্তু এটা আই-পিপিই-এর ক্ষেত্রে প্রযোজ্য নয়.

আপনি যদি pull_request_target ব্যবহার করেন, তাহলে ডিফল্ট চেকআউট হবে বেস কোড। কিন্তু আপনি যদি কন্ট্রিবিউটেড কোডের (পিআর কোড) উপর কিছু চেক ভ্যালিডেট করতে চান, তাহলে আপনাকে স্পষ্টভাবে পিআর কোডটি চেকআউট করতে হবে। সুতরাং, যদি পিআর কোডটি পুল-রিকোয়েস্ট টার্গেট দ্বারা কল করা কোনো শেল স্ক্রিপ্ট পরিবর্তন করে থাকে, তাহলে সেই চেকটি বাতিল হয়ে যাবে। pipeline, “ভিত্তি” (নিরাপদ) pipeline “সংশোধিত” শেল স্ক্রিপ্টটি চালু করবে → পরোক্ষ পিপিই!!

এর সমাধানটা আরেকটু জটিল (pull_request_target-এর মতো কোনো জাদুকরী সমাধান নেই)। 

আমাদের pipeline এখন D-PPE এর জন্য নিরাপদ, কারণ আমরা pull_request_target ব্যবহার করছি। কিন্তু এটি এখনও I-PPE এর জন্য ঝুঁকিপূর্ণ। 

আমাদের পরীক্ষার উদাহরণে, বিল্ড তৈরি করার জন্য মূলত পিআর কোড চেকআউট করতে হয়, কিন্তু পরীক্ষাগুলো বিল্ড দ্বারা তৈরি আর্টিফ্যাক্টের উপর চালানো হয়। 

তাই .. দুটো কোডবেসই দেখে নিচ্ছেন না কেন? 

  • পিআর কোডটি দেখুন, কারণ আমরা এই অবদানকৃত কোডটিই বিল্ড এবং টেস্ট করতে চাই।
  • মূল সংস্করণটি চালানোর জন্য বেস কোডটি দেখুন। pipeline এবং বিল্ড/টেস্ট স্ক্রিপ্টগুলি 

এটি করা হতে পারে ওই কোডবেসগুলো বিভিন্ন ফোল্ডারে চেক আউট করা হচ্ছেবেস কোডটি রুট ফোল্ডারে এবং পিআর (PR) অন্য একটি ফোল্ডারে চেক আউট করা হতে পারে। এই ক্ষেত্রে, আমরা রুট ফোল্ডার থেকে বিল্ড এবং টেস্ট স্ক্রিপ্টটি নতুন ফোল্ডারে রাখা কোডের উপর চালাব।

অবশ্যই, এটা একটা সহজ সমাধান!! কিন্তু, শেখার উদ্দেশ্যে আমি একটি বেশ আকর্ষণীয় বিকল্প উপস্থাপন করতে চাই (...) 

GitHub ওয়ার্কফ্লো_রান ট্রিগার ইভেন্ট

ব্যতীত পুল_রিকোয়েস্ট_টার্গেটগিটহাব আরেকটি ট্রিগার ইভেন্ট প্রদান করে: ওয়ার্কফ্লো_রানএই অনুষ্ঠানটি অনুমতি দেয় একটির মৃত্যুদণ্ড pipeline অন্যের সাথে শর্তযুক্ত pipelineতার মৃত্যুদণ্ড

ওয়ার্কফ্লো_রান এবং পুল_রিকোয়েস্ট_টার্গেট ট্রিগারগুলো একটি দিক থেকে একই রকম: উভয়ই প্রিভিলেজড মোডে কার্যকর হবে এবং, পিআর পরিবর্তন সত্ত্বেও, ভিত্তি pipeline মৃত্যুদণ্ড কার্যকর করা হবে !! 

চলুন আমাদের বর্তমান অবস্থা দেখি pipeline:

				
					name: PR TARGET CI


on:
  pull_request_target:
    branches: [ main ]


env:
  MY_SECRET: ${{ secrets.MY_SECRET }}
 
jobs:
  prt_build_test_and_merge:
    runs-on: ubuntu-latest


    steps:
      # checkout PR code
      - name: Checkout repository
        uses: actions/checkout@v4
        with:


          # This is to get the PR code instead of the repo code
          ref: ${{ github.event.pull_request.head.sha }}


      # Simulation of a compilation
      - name: Building ...
        run: |
          mkdir ./bin
          touch ./bin/mybin.exe
          ls -lR
     
      # Simulation of running tests
      - name: Running tests ...
        id : run_tests
        run: |
          echo Running tests..
          chmod +x runtests.sh
          ./runtests.sh 
          echo Tests executed.
         
      #
      # Let’s omit the check conditions at this moment …
      #
      - name: pr_check_conditions_to_merge
        [...]
				
			

বিল্ড সেকশনটি ডি-পিপিই (D-PPE)-এর জন্য নিরাপদ, কিন্তু টেস্ট সেকশনটি এখনও আই-পিপিই (I-PPE)-এর জন্য ঝুঁকিপূর্ণ।

সার্জারির pipeline এটি নিজেই ডি-পিপিই এর জন্য নিরাপদ কারণ পুল_রিকোয়েস্ট_টার্গেট ট্রিগার। কিন্তু একটি বাহ্যিক শেল স্ক্রিপ্ট কল করার কারণে পরীক্ষার ধাপটি এখনও I-PPE-এর ঝুঁকিতে রয়েছে।

আই-পিপিই এড়ানো 

উপরোক্তের উদ্দেশ্য pipeline অবদানকৃত কোড বিল্ড ও টেস্ট করা এবং PPE অনুযায়ী নিরাপদ থাকা। 

তাই .. কেন ভাগ করবেন না pipeline দুটি ভাগে? একটি নির্মাণের জন্য এবং অন্যটি পরীক্ষার জন্য..

  • 1 ম pipeline (CI তৈরি করুন) হবে পিআর কোডটি দেখুন (এটি বিল্ড করতে)বিল্ডটি সম্পন্ন করুন এবং একটি আর্টিফ্যাক্ট তৈরি করুন।
  • ২ য় pipeline (টেস্ট সিআই) হবে বেস কোডটি চেকআউট করুন (শেল স্ক্রিপ্ট পরিবর্তন এড়াতে)। এবং আর্টিফ্যাক্টটির উপর মূল স্ক্রিপ্টগুলো চালান। 
  • টেস্ট সিআই সিঙ্ক্রোনাইজ করতে pipeline বিল্ড সিআই (Build CI) এর পরে চালানোর জন্য pipelineআমরা ব্যবহার করব ওয়ার্কফ্লো_রান ট্রিগার 
পিপিই৮

এইভাবে:

  • pipeline CI তৈরি করুন is নিরাপদ উভয় ডি-পিপিই (কারণে পুল_রিকোয়েস্ট_টার্গেট) এবং আই-পিপিই (কারণ এটি আর শেল স্ক্রিপ্টটি চালায় না)।
  • pipeline টেস্ট সিআই এছাড়াও নিরাপদ উভয় ডি-পিপিই (কারণে ওয়ার্কফ্লো_রান) এবং আই-পিপিই (কারণ এটি মূল শেল স্ক্রিপ্টটি পেতে বেস কোডটি চেকআউট করে) 

চলুন উভয়ের কোড দেখি pipelineএই পরিবর্তনগুলো অনুসারে …

1st pipeline (CI তৈরি করুন):

				
					name: Build CI


on:
  pull_request_target:
    branches: [ main ]


env:
  MY_SECRET: ${{ secrets.MY_SECRET }}
  GITHUB_PAT: ${{ secrets.GH_PAT }}
 
jobs:
               
  prt_build_and_upload:
    runs-on: ubuntu-latest
    steps:
      - name: Checking out PR code
        uses: actions/checkout@v4
        if: ${{ github.event_name == 'pull_request_target' }}
        with:
          # This is to get the PR code instead of the repo code
          ref: ${{ github.event.pull_request.head.sha }}


      - name: Building ...
        run: |
          mkdir ./bin
          touch ./bin/mybin.exe
	    # Save some PR info for later use by the 2nd pipeline
          echo "${{github.event.pull_request.title}}" > ./bin/PR_TITLE.txt
          echo "${{github.event.number}}" > ./bin/PR_ID.txt
 
	# Upload the binary as a pipeline artifact
      - name: Archive building artifacts
        uses: actions/upload-artifact@v3
        with:
          name: archive-bin
          path: |
            bin
				
			

2nd pipeline (পরীক্ষা CI):

				
					ame: Test CI


on:
  workflow_run:
    workflows: [ 'PR TARGET CI' ]
    types: [completed]
   
env:
  MY_SECRET: ${{ secrets.MY_SECRET }}
  GITHUB_PAT: ${{ secrets.GH_PAT }}




jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
 


      # By default, checks out base code (not PR code)
      - name: Checkout repository
        uses: actions/checkout@v4


	# Download the artifact
      - name: 'Download artifact'
        uses: actions/github-script@v6
        with:
          script: |
            let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
               owner: context.repo.owner,
               repo: context.repo.repo,
               run_id: context.payload.workflow_run.id,
            });
            let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
              return artifact.name == "archive-bin"
            })[0];
            let download = await github.rest.actions.downloadArtifact({
               owner: context.repo.owner,
               repo: context.repo.repo,
               artifact_id: matchArtifact.id,
               archive_format: 'zip',
            });
            let fs = require('fs');
            fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/myartifact.zip`, Buffer.from(download.data));


	# Unzip the artifact
      - name: 'Unzip artifact'
        run: |
          unzip -o myartifact.zip


      # Runs tests
      - name: Running tests ...
        id : run_tests
        run: |
          echo Running tests..
          chmod +x runtests.sh
          ./runtests.sh
          echo Tests executed.


#
      # Let’s omit the check conditions at this moment …
      #
      - name: pr_check_conditions_to_merge
        [...]

				
			

বাহ... দারুণ সমাধান!! কিন্তু... আমরা কি নিরাপদ? আমার ভয় হচ্ছে যে না 😭

সত্যিই, আমরা একটি নতুন দুর্বলতা যুক্ত করেছি!! সেটি কোনটি? এটাই হবে আমাদের পরবর্তী পোস্টের বিষয় 🙂 … সাথেই থাকুন!! 

দ্রষ্টব্য: দুঃখিত, আমি চুপ থাকতে পারি না 🤐 ..আপনি কি শুনেছেন যে আর্টিফ্যাক্ট পয়জনিং ? 😂

আর্টিফ্যাক্ট পয়জনিং এবং কোড ইনজেকশন

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (III)

সফটওয়্যার অ্যাটেস্টেশনের মাধ্যমে আর্টিফ্যাক্ট পয়জনিং থেকে সুরক্ষা

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (IV)

বিষাক্ত Pipeline মৃত্যুদণ্ড (PPE)

একটি গভীর ডুব CI/CD Pipelineদুর্বলতা (I)
sca-tools-software-composition-analysis-tools
আপনার সফটওয়্যারের ঝুঁকিগুলোকে অগ্রাধিকার দিন, প্রতিকার করুন এবং সুরক্ষিত করুন।
আপনার বিনামূল্যে অ্যাকাউন্টটি নিন।
কোন ক্রেডিট কার্ড প্রয়োজন নেই

আপনার সফটওয়্যার উন্নয়ন ও বিতরণ সুরক্ষিত করুন

Xygeni প্রোডাক্ট স্যুটের সাথে