Langsung ke konten
KamusNgoding
Menengah Github-actions 6 menit baca

Cara Mengatasi Error 'Permission Denied' di GitHub Actions

#github-actions #error #permission #debug

Cara Mengatasi Error ‘Permission Denied’ di GitHub Actions

Pendahuluan

Kamu sudah menulis workflow GitHub Actions dengan semangat, menekan tombol push, lalu… bam — merah. Log penuh dengan pesan Permission denied (publickey) atau Error: Resource not accessible by integration. Menyebalkan, bukan?

Error Permission Denied adalah salah satu halangan paling umum yang dihadapi developer saat bekerja dengan GitHub Actions. Kabar baiknya: hampir semua kasusnya punya solusi yang jelas begitu kamu tahu penyebabnya.

Artikel ini membahas cara mendiagnosis dan memperbaiki error permission di GitHub Actions secara tuntas — dari konfigurasi GITHUB_TOKEN, manajemen SSH key, hingga Personal Access Token (PAT). Jika kamu masih asing dengan dasar-dasar workflow GitHub Actions, ada baiknya membaca dulu Pengenalan GitHub Actions: Otomasi CI/CD untuk Pemula sebelum melanjutkan.


Memahami Penyebab Umum Error ‘Permission Denied’

Error permission di GitHub Actions umumnya muncul dari tiga sumber utama:

1. GITHUB_TOKEN Tidak Punya Izin yang Cukup

Sejak akhir 2022, GitHub memperketat kebijakan default GITHUB_TOKEN. Secara default, token ini hanya punya akses read-only di banyak resource. Jika workflow kamu mencoba push ke repo, create release, atau menulis comment di PR, kamu perlu mendeklarasikan permission secara eksplisit.

2. SSH Key Tidak Terkonfigurasi dengan Benar

Workflow yang mencoba melakukan git clone ke repo privat atau men-deploy ke server via SSH akan gagal jika private key tidak dikirim ke runner dengan benar.

3. Personal Access Token (PAT) Kadaluarsa atau Scope-nya Salah

PAT yang digunakan sebagai secret bisa expired, atau scope yang dipilih saat membuat token tidak mencakup operasi yang dibutuhkan workflow.


Mengonfigurasi Izin GITHUB_TOKEN dengan Benar

GITHUB_TOKEN adalah token otomatis yang dibuat GitHub untuk setiap run workflow. Token ini tidak perlu dibuat manual, tapi kamu harus mendefinisikan izinnya dengan jelas.

Cara Set Permission di Level Workflow

# .github/workflows/deploy.yml
name: Deploy Application

on:
  push:
    branches: [main]

# Set permission di level workflow (berlaku untuk semua job)
permissions:
  contents: write      # Izin baca dan tulis ke repo
  pull-requests: write # Izin comment atau update PR
  issues: write        # Izin membuat/update issue
  packages: write      # Izin publish ke GitHub Packages

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Push perubahan ke repo
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add .
          git commit -m "chore: update generated files [skip ci]"
          git push
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Cara Set Permission di Level Job

Jika hanya satu job yang butuh permission tambahan, set di level job agar job lain tetap minimal:

jobs:
  build:
    runs-on: ubuntu-latest
    # Job ini hanya perlu baca
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build

  release:
    runs-on: ubuntu-latest
    needs: build
    # Hanya job release yang butuh write
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:
          files: dist/**

Tips: Selalu gunakan prinsip least privilege — berikan hanya izin yang benar-benar dibutuhkan. Bayangkan kamu membangun sistem deployment seperti yang digunakan tim e-commerce besar; semakin ketat permission-nya, semakin aman pipeline-nya.


Mengelola Kunci SSH dan Personal Access Token (PAT)

Menggunakan SSH Key untuk Deploy ke Server

Untuk deploy ke VPS atau server via SSH, kamu perlu menyimpan private key sebagai GitHub Secret.

Langkah 1: Generate SSH key pair

# Generate di lokal, JANGAN gunakan passphrase
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key -N ""

# Tampilkan private key (untuk disimpan di GitHub Secret)
cat ~/.ssh/deploy_key

# Tampilkan public key (untuk ditambah ke server)
cat ~/.ssh/deploy_key.pub

Langkah 2: Tambahkan public key ke server

# Di server tujuan deploy
echo "isi_public_key_kamu" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Langkah 3: Simpan private key di GitHub Secrets

Pergi ke Settings → Secrets and variables → Actions → New repository secret, beri nama SSH_PRIVATE_KEY, lalu paste isi private key.

Langkah 4: Gunakan di workflow

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup SSH
        uses: webfactory/[email protected]
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

      - name: Deploy ke server
        run: |
          ssh -o StrictHostKeyChecking=no [email protected] << 'EOF'
            cd /var/www/myapp
            git pull origin main
            npm ci --production
            pm2 restart myapp
          EOF

Menggunakan PAT untuk Operasi Lintas Repo

GITHUB_TOKEN tidak bisa mengakses repo lain. Untuk itu, gunakan PAT:

jobs:
  sync-docs:
    runs-on: ubuntu-latest
    steps:
      - name: Clone repo dokumentasi (repo lain)
        uses: actions/checkout@v4
        with:
          repository: org-kamu/docs-repo
          token: ${{ secrets.PAT_CROSS_REPO }}
          path: docs

      - name: Sync file
        run: cp -r src/docs/* docs/

      - name: Push ke docs-repo
        run: |
          cd docs
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add .
          git commit -m "sync: update docs from main repo"
          git push

Saat membuat PAT, pilih scope yang tepat:

  • repo — akses penuh ke repo privat
  • read:packages / write:packages — untuk GitHub Packages
  • workflow — untuk update file workflow

Contoh Kasus Nyata

Kasus: Workflow Gagal Push ke Branch Setelah Bump Version

Ini kasus klasik. Workflow release mencoba commit file package.json yang sudah di-bump, tapi gagal karena permission default read-only.

name: Auto Release

on:
  push:
    branches: [main]

permissions:
  contents: write  # ← Ini yang menyelamatkan!

jobs:
  bump-and-release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Bump version
        run: |
          npm version patch --no-git-tag-version
          VERSION=$(node -p "require('./package.json').version")
          echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV

      - name: Commit dan push
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add package.json package-lock.json
          git commit -m "chore: bump version to v${{ env.NEW_VERSION }}"
          git push
          git tag "v${{ env.NEW_VERSION }}"
          git push --tags

Troubleshooting: Error yang Sering Muncul

Error: Resource not accessible by integration

Penyebab: GITHUB_TOKEN tidak punya izin write untuk resource yang diakses. Ini paling sering terjadi saat workflow mencoba comment di PR, push ke repo, atau membuat release tanpa mendeklarasikan permission secara eksplisit.

Solusi:

# Tambahkan blok permissions di level workflow atau job
name: My Workflow

on:
  push:
    branches: [main]

permissions:
  pull-requests: write
  contents: write

jobs:
  my-job:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Comment di PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: 'Deployment berhasil!'
            })

Catatan: Workflow yang dipicu dari fork tidak bisa menggunakan secrets atau write permission. Gunakan event pull_request_target dengan hati-hati untuk kasus ini.


Permission denied (publickey) Saat SSH

Penyebab: Private key tidak ter-load dengan benar di SSH agent, atau public key belum ditambahkan ke authorized_keys di server tujuan. Bisa juga terjadi karena format private key salah saat disimpan di GitHub Secrets (ada karakter tambahan atau newline yang hilang).

Solusi:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Setup SSH Agent
        uses: webfactory/[email protected]
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

      - name: Debug SSH Connection
        run: |
          # Verifikasi key sudah ter-load
          ssh-add -l

          # Test koneksi ke server (verbose mode)
          ssh -vT [email protected] 2>&1 | head -30

      - name: Deploy
        run: ssh [email protected] "cd /var/www/app && git pull"

Penting: Private key harus diawali dengan -----BEGIN OPENSSH PRIVATE KEY----- dan diakhiri dengan -----END OPENSSH PRIVATE KEY-----, termasuk newline di akhir baris terakhir.


fatal: could not read Username Saat git push

Penyebab: URL remote menggunakan HTTPS tapi tidak ada token authentication, atau token tidak di-pass ke perintah git. Ini umum terjadi saat actions/checkout menggunakan default settings dan kamu mencoba push manual.

Solusi:

jobs:
  push-changes:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - name: Buat perubahan
        run: echo "update" >> changelog.txt

      - name: Push dengan token explicit
        run: |
          # Set URL remote dengan token embed
          git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add changelog.txt
          git commit -m "chore: update changelog"
          git push origin main

PAT expired — Workflow Tiba-tiba Gagal Setelah Berjalan Normal

Penyebab: PAT yang digunakan sebagai secret sudah expired. GitHub mengirim email notifikasi beberapa hari sebelum expired, tapi sering terlewat.

Solusi:

# 1. Buat PAT baru di GitHub:
#    Settings → Developer settings → Personal access tokens → Generate new token

# 2. Pilih scope yang diperlukan (contoh untuk akses repo):
#    ✓ repo (Full control of private repositories)
#    ✓ workflow (Update GitHub Action workflows)

# 3. Perbarui di GitHub Secret:
#    Repo → Settings → Secrets and variables → Actions
#    → Klik secret yang berisi PAT lama → Update

# 4. Untuk mencegah di masa depan, gunakan Fine-grained PAT
#    dengan masa berlaku lebih panjang (max 1 tahun),
#    atau pertimbangkan GitHub Apps untuk otomasi jangka panjang

Pertanyaan yang Sering Diajukan

Apa perbedaan GITHUB_TOKEN dengan Personal Access Token (PAT)?

GITHUB_TOKEN adalah token sementara yang dibuat otomatis oleh GitHub untuk setiap workflow run dan otomatis kadaluarsa setelah run selesai — kamu tidak perlu membuatnya manual. PAT dibuat secara manual oleh pengguna, punya masa berlaku yang bisa diatur, dan bisa mengakses resource di luar repo tempat workflow berjalan, termasuk repo lain atau organisasi lain.

Mengapa GITHUB_TOKEN tidak bisa trigger workflow lain?

Ini adalah batasan keamanan yang disengaja oleh GitHub. Jika GITHUB_TOKEN bisa memicu workflow lain secara berantai, ada risiko infinite loop yang bisa menghabiskan menit gratis GitHub Actions tanpa henti. Untuk memicu workflow lain, gunakan PAT dengan scope workflow, atau manfaatkan event repository_dispatch yang dirancang untuk keperluan ini.

Bagaimana cara tahu permission apa yang dibutuhkan workflow saya?

Cara paling praktis: jalankan workflow tanpa blok permissions, baca pesan error di log, lalu tambahkan permission yang diminta. Kamu juga bisa mulai dari permissions: {} (kosong/minimal), lalu tambahkan izin satu per satu sampai workflow berhasil berjalan. GitHub juga mencantumkan daftar permission yang tersedia di dokumentasi resminya.

Apakah aman menyimpan SSH private key di GitHub Secrets?

Ya, GitHub Secrets dienkripsi menggunakan LibSodium dan nilai secret tidak pernah ditampilkan di log workflow. Namun, pastikan kamu membuat key pair khusus untuk deployment (jangan gunakan key SSH personal), tambahkan public key-nya hanya ke server yang relevan, dan rotasi key secara berkala. Dengan praktik ini, risiko keamanan dapat diminimalkan secara signifikan.

Bagaimana menangani permission di workflow yang dipicu dari Pull Request fork?

Workflow yang dipicu dari fork secara default tidak mendapat akses ke secrets dan hanya punya read permission — ini adalah perlindungan bawaan GitHub. Untuk kasus ini, gunakan event pull_request_target yang berjalan di konteks repo base, bukan fork. Namun berhati-hatilah: jangan pernah menjalankan kode yang belum diverifikasi dari PR fork di dalam pull_request_target karena berisiko eksekusi kode berbahaya.

Bagaimana cara debug jika tidak tahu permission mana yang kurang?

Aktifkan debug logging dengan menambahkan secret ACTIONS_STEP_DEBUG bernilai true di repository. Workflow akan menghasilkan log yang jauh lebih detail, termasuk informasi tentang token scope dan resource yang dicoba diakses. Setelah masalah teridentifikasi, hapus secret tersebut agar log tidak terlalu panjang di run normal.


Kesimpulan

Error Permission Denied di GitHub Actions hampir selalu bisa diatasi dengan tiga langkah: (1) pahami apakah sumber masalahnya di GITHUB_TOKEN, SSH key, atau PAT; (2) deklarasikan permission secara eksplisit menggunakan blok permissions; (3) pastikan secret tersimpan dengan format yang benar dan belum expired.

Prinsip least privilege — memberikan hanya izin minimum yang diperlukan — bukan cuma best practice keamanan, tapi juga membantu kamu mendiagnosis masalah lebih cepat karena pesan error-nya menjadi lebih spesifik. Jika kamu ingin memperdalam pemahaman tentang otomasi pipeline secara menyeluruh, lihat juga Konsep Dasar CI/CD: Continuous Integration dan Deployment untuk Pemula untuk membangun pipeline yang lebih tangguh dan terstruktur.

Selamat debugging dan jangan menyerah — setiap error yang berhasil kamu pecahkan adalah bukti nyata bahwa kemampuanmu terus berkembang. Jika masih ada pertanyaan atau kamu menemukan kasus yang belum dibahas di sini, jangan ragu untuk eksplorasi artikel-artikel lainnya di KamusNgoding!

Artikel Terkait