Thuta Learning
CI/CD with GitHub Actions
BasicDevOps & Toolsbeginner

Marketplace က Actions တွေကို လုံခြုံစွာ သုံးခြင်း

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • Marketplace က Actions တွေကို လုံခြုံစွာ သုံးခြင်း concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • Diagram ကို ဖတ်ပြီး pipeline ထဲမှာ job တွေ ဘယ်လိုစီးဆင်းပြီး ဘာက gate လုပ်သလဲ ခြေရာခံနိုင်ရန်
  • Workflow file ကို ဖတ်ပြီး ဘယ် job တွေ ဘယ်အစဉ်လိုက် run မလဲ ကြိုတင်ပြောနိုင်ရန်

နားလည်ထားရမယ့် အချက်

Action တစ်ခုဆိုတာ package လုပ်ထားပြီး ပြန်သုံးလို့ရတဲ့ step တစ်ခုပါ - repository တစ်ခုထဲမှာ action.yml နဲ့ code (JavaScript, Docker image, ဒါမှမဟုတ် composite step စာရင်း) ရှိတယ်။ uses: actions/checkout@v4 လို့ ရေးလိုက်တဲ့အခါ Actions က github.com/actions/checkout repository ကို သွားရှာပြီး၊ v4 ဆိုတဲ့ ref ကို resolve လုပ်ကာ၊ အဲဒီ code ကို သင့် runner ပေါ်မှာ run ပါတယ်။ ဒါက စိတ်ဝင်စားစရာ အချက်တစ်ခုကို ပြပါတယ် - သင်ဟာ တခြားလူတစ်ယောက်ရဲ့ code ကို သင့် repository token နဲ့ secret တွေ ရနိုင်တဲ့ environment ထဲမှာ run ခွင့်ပေးလိုက်တာပါ။

@ အနောက်က ref ဟာ branch, tag, ဒါမှမဟုတ် commit SHA တစ်ခု ဖြစ်နိုင်ပါတယ်။ @v4 ဟာ version နံပါတ်လို မြင်ရပေမယ့် တကယ်တော့ git tag တစ်ခုသာ ဖြစ်ပြီး၊ tag ဆိုတာ ရွှေ့လို့ရပါတယ်။ actions/* တွေဟာ v4 tag ကို v4.1.7, v4.2.0 စသဖြင့် release အသစ်ထွက်တိုင်း ရှေ့တိုးရွှေ့ပေးလေ့ရှိပါတယ်။ ဒါက အားသာချက်ပါ - bug fix နဲ့ security patch တွေကို ကိုယ်ဘာမှ မလုပ်ဘဲ ရနေပါတယ်။ တစ်ချိန်တည်းမှာ ဒါက အားနည်းချက်လည်း ဖြစ်ပါတယ် - သင့် YAML စာကြောင်း တစ်လုံးမှ မပြောင်းဘဲနဲ့ မနက်ဖြန်ကျရင် တခြား code တစ်ခု run နေနိုင်ပါတယ်။ maintainer account တစ်ခု အလုအယူခံရရင် (တကယ် ဖြစ်ဖူးပါတယ်) attacker က tag ကို malicious commit ဆီ ပြန်ညွှန်လိုက်ရုံနဲ့ အဲဒီ action သုံးထားတဲ့ repository ထောင်ပေါင်းများစွာက အလိုအလျောက် ကူးစက်သွားပါတယ်။

ဒါကြောင့် လက်ရှိ အကြံပြုချက်က - first-party actions/* တွေကို major tag နဲ့ pin လုပ်တာ လက်ခံနိုင်ပေမယ့်၊ third-party action တွေကိုတော့ commit SHA အပြည့် (၄၀ လုံး) နဲ့ pin လုပ်ပါ။ SHA က content ကို ကိုယ်စားပြုလို့ ရွှေ့လို့ မရပါဘူး။ ဖတ်ရခက်တဲ့ အားနည်းချက်ကို နောက်ကွယ်မှာ # v1.9.1 လိုမျိုး comment ထည့်ပြီး ဖြေရှင်းနိုင်ပြီး၊ Dependabot က အဲဒီ SHA တွေကို PR နဲ့ update ပေးနိုင်ပါတယ်။ action အသစ်တစ်ခုကို မထည့်ခင် အနည်းဆုံး - နောက်ဆုံး commit ဘယ်တုန်းကလဲ၊ maintainer ဘယ်နှစ်ယောက်ရှိလဲ၊ တကယ် လိုအပ်လား (run: တစ်ကြောင်းနဲ့ ရနိုင်လား) ဆိုတာ မေးကြည့်ပါ။

text
HOW A USES REFERENCE RESOLVES, AND WHERE IT CAN MOVE
----------------------------------------------------
  uses: actions/checkout@v4
         |        |       |
       owner     repo    ref
                          |
                          v
                  +---------------+
                  |  git tag v4   |   <-- a MOVING pointer
                  +---------------+
                          |
                     resolves to
                          v
          +-------------------------------+
          | commit 1a2b3c4... (today)     |
          +-------------------------------+

  the owner (or whoever steals the account) can re-point it:

                  +---------------+
                  |  git tag v4   | - - - - +
                  +---------------+         |
                                            v
          +-------------------------------+ |
          | commit 1a2b3c4... (yesterday) | |
          +-------------------------------+ |
          | commit deadbee... (malicious)  |<+
          +-------------------------------+

  your YAML line never changed. The code it runs did.

  uses: some-owner/some-action@<40-char-sha>   # cannot move

လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်

အောက်က workflow မှာ action နှစ်မျိုးလုံးကို တွေ့ရပါမယ်။ actions/checkout, actions/setup-node, actions/cache, actions/upload-artifact နဲ့ pnpm/action-setup တို့ကို major tag နဲ့ pin လုပ်ထားပါတယ်။ ဒါတွေဟာ GitHub ကိုယ်တိုင် (နဲ့ pnpm project) ထိန်းသိမ်းထားတာမို့ tag pin ကို လက်ခံနိုင်ပါတယ်။

cache step ကို သေချာကြည့်ပါ။ key ထဲမှာ ${{ runner.os }} နဲ့ ${{ hashFiles('pnpm-lock.yaml') }} ပါဝင်ပါတယ် - hashFiles က အဲဒီ file ရဲ့ content hash ကို ထုတ်ပေးလို့၊ dependency တွေ ပြောင်းသွားတိုင်း key အသစ် ဖြစ်ပြီး cache အသစ် ဆောက်ပါတယ်။ restore-keys ကတော့ တိတိကျကျ ကိုက်တဲ့ key မရှိတဲ့အခါ prefix နဲ့ အနီးစပ်ဆုံးကို ပြန်ယူဖို့ပါ - lockfile ပြောင်းသွားပေမယ့် အများစုက အတူတူဖြစ်နေတဲ့အခါ အထောက်အကူ ဖြစ်ပါတယ်။

နောက်ဆုံးမှာ comment နဲ့ ပြထားတဲ့ SHA pin ပုံစံကို သတိပြုပါ။ third-party action တစ်ခုကို ထည့်တော့မယ်ဆိုရင် အဲဒီ repository ရဲ့ release page ကို သွား၊ ကိုယ်လိုချင်တဲ့ tag ရဲ့ commit SHA အပြည့်ကို ကူးယူ၊ @ နောက်မှာ ထည့်ပြီး၊ ဘယ် tag ကို ကိုယ်စားပြုလဲ ကို comment နဲ့ မှတ်ထားပါ။ ကိုယ့် repository မှာ .github/dependabot.yml ထဲ package-ecosystem: "github-actions" ထည့်ထားရင် Dependabot က အဲဒီ pin တွေကို အလိုအလျောက် update PR ဖွင့်ပေးလို့ လုံခြုံမှုနဲ့ ခေတ်မီမှု နှစ်ခုလုံး ရနိုင်ပါတယ်။

အတူတူ စမ်းရေးကြည့်မယ်

yaml
name: Pinned Actions

on:
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: First-party action, pinned to a major tag
        uses: actions/checkout@v4

      - name: Set up pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: pnpm

      - name: Cache the framework build output
        uses: actions/cache@v4
        with:
          path: .next/cache
          key: next-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
          restore-keys: |
            next-${{ runner.os }}-

      - run: pnpm install --frozen-lockfile
      - run: pnpm build

      - name: Upload the build output
        uses: actions/upload-artifact@v4
        with:
          name: web-build
          path: .next/

      # A third-party action should be pinned by full commit SHA, like this:
      #
      #   - name: Publish the test report
      #     uses: some-owner/some-action@<40-character-commit-sha>  # v1.9.1
      #
      # Copy the SHA from the action's release page, and keep the tag it
      # matched in the trailing comment so humans can still read the file.
You should see
main ကို target ထားတဲ့ pull_request တိုင်းမှာ build job တစ်ခုတည်း run ပါတယ်။ step တွေက အစဉ်လိုက် - repository checkout၊ pnpm 9 တပ်ဆင်၊ Node 20 တပ်ဆင်ပြီး pnpm store ကို cache လုပ်၊ .next/cache ကို lockfile hash အပေါ်မူတည်တဲ့ key နဲ့ ပြန်ယူ၊ dependency install၊ build၊ ပြီးရင် .next/ ကို web-build ဆိုတဲ့ artifact အဖြစ် တင်ပါတယ်။ lockfile မပြောင်းရင် cache key တူညီလို့ cache hit ဖြစ်ပြီး၊ lockfile ပြောင်းရင် key အသစ်ဖြစ်လို့ restore-keys prefix နဲ့ အနီးစပ်ဆုံး cache ကို ပြန်ယူပြီးမှ run အဆုံးမှာ cache အသစ် သိမ်းပါတယ်။ step တစ်ခုခု ကျရှုံးရင် job ကျရှုံးပြီး pull_request မှာ check ကျရှုံးကြောင်း ပြပါတယ်။

၅ မိနစ် စမ်းကြည့်

ကိုယ်သုံးနေတဲ့ repository တစ်ခုရဲ့ workflow file တွေထဲက uses: စာကြောင်းအားလုံးကို စာရင်းလုပ်ပါ။ actions/* မဟုတ်တဲ့ တစ်ခုစီအတွက် - နောက်ဆုံး commit ဘယ်တုန်းကလဲ၊ star ဘယ်လောက်ရှိလဲ၊ maintainer ဘယ်နှစ်ယောက်လဲ စစ်ကြည့်ပါ။ ပြီးရင် တစ်ခုကို ရွေးပြီး သူ့ release page ကနေ commit SHA အပြည့်ကို ယူကာ tag pin ကို SHA pin အဖြစ် ပြောင်းပြီး၊ ဘယ် tag ကို ကိုယ်စားပြုလဲ comment ထည့်ပါ။ .github/dependabot.yml ကိုလည်း github-actions ecosystem နဲ့ ထည့်ကြည့်ပါ။

သတိလေးတစ်ချက်

uses: some-owner/some-action@main လို့ branch ကို ညွှန်ထားတာ - maintainer က main ကို commit တစ်ခု တင်လိုက်တာနဲ့ သင့် pipeline ထဲမှာ အဲဒီ code က ချက်ချင်း run ပါတယ်။

third-party action တစ်ခုကို secret တွေ ပေးထားပြီး code ကို လုံးဝ မဖတ်ကြည့်တာ - action ဟာ သင့် runner ပေါ်မှာ full access နဲ့ run နေတာဖြစ်လို့ npm package တစ်ခု install လုပ်တာနဲ့ တူညီတဲ့ ယုံကြည်မှု လိုအပ်ပါတယ်။

GitHub Docs - Security hardening for GitHub ActionsCI/CD with GitHub Actions

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • uses: some-owner/some-action@main လို့ branch ကို ညွှန်ထားတာ - maintainer က main ကို commit တစ်ခု တင်လိုက်တာနဲ့ သင့် pipeline ထဲမှာ အဲဒီ code က ချက်ချင်း run ပါတယ်။
  • third-party action တစ်ခုကို secret တွေ ပေးထားပြီး code ကို လုံးဝ မဖတ်ကြည့်တာ - action ဟာ သင့် runner ပေါ်မှာ full access နဲ့ run နေတာဖြစ်လို့ npm package တစ်ခု install လုပ်တာနဲ့ တူညီတဲ့ ယုံကြည်မှု လိုအပ်ပါတယ်။
  • Workflow ကို production branch ပေါ် တိုက်ရိုက်မစမ်းဘဲ branch (သို့) test repository တွင် အရင်အတည်ပြုပါ။

လေ့ကျင့်ခန်း

ကိုယ်သုံးနေတဲ့ repository တစ်ခုရဲ့ workflow file တွေထဲက uses: စာကြောင်းအားလုံးကို စာရင်းလုပ်ပါ။ actions/* မဟုတ်တဲ့ တစ်ခုစီအတွက် - နောက်ဆုံး commit ဘယ်တုန်းကလဲ၊ star ဘယ်လောက်ရှိလဲ၊ maintainer ဘယ်နှစ်ယောက်လဲ စစ်ကြည့်ပါ။ ပြီးရင် တစ်ခုကို ရွေးပြီး သူ့ release page ကနေ commit SHA အပြည့်ကို ယူကာ tag pin ကို SHA pin အဖြစ် ပြောင်းပြီး၊ ဘယ် tag ကို ကိုယ်စားပြုလဲ comment ထည့်ပါ။ .github/dependabot.yml ကိုလည်း github-actions ecosystem နဲ့ ထည့်ကြည့်ပါ။

You'll know it worked when: main ကို target ထားတဲ့ pull_request တိုင်းမှာ build job တစ်ခုတည်း run ပါတယ်။ step တွေက အစဉ်လိုက် - repository checkout၊ pnpm 9 တပ်ဆင်၊ Node 20 တပ်ဆင်ပြီး pnpm store ကို cache လုပ်၊ .next/cache ကို lockfile hash အပေါ်မူတည်တဲ့ key နဲ့ ပြန်ယူ၊ dependency install၊ build၊ ပြီးရင် .next/ ကို web-build ဆိုတဲ့ artifact အဖြစ် တင်ပါတယ်။ lockfile မပြောင်းရင် cache key တူညီလို့ cache hit ဖြစ်ပြီး၊ lockfile ပြောင်းရင် key အသစ်ဖြစ်လို့ restore-keys prefix နဲ့ အနီးစပ်ဆုံး cache ကို ပြန်ယူပြီးမှ run အဆုံးမှာ cache အသစ် သိမ်းပါတယ်။ step တစ်ခုခု ကျရှုံးရင် job ကျရှုံးပြီး pull_request မှာ check ကျရှုံးကြောင်း ပြပါတယ်။

Marketplace က Actions တွေကို လုံခြုံစွာ သုံးခြင်း | Thuta Learning