Cara Memakai Secrets GitHub Actions: Variables, Scope, Fork
Cara memakai secrets GitHub Actions: beda secret dan variable, scope repository dan environment, masking di log, pull request dari fork, dan GITHUB_TOKEN.
Oleh Matthews WongDitinjau pada
Poin utama
Secrets GitHub Actions adalah nilai terenkripsi, seperti API key dan SSH key, yang dibaca job lewat context secrets; runner menggantinya dengan *** di log. Configuration variable menyimpan pengaturan yang tidak sensitif, dibaca lewat vars, dan tercetak apa adanya. Keduanya ada di level organization, repository, atau environment, level paling rendah yang menang, dan pull request dari fork tidak mendapat secret.
Setelah pelajaran ini, Anda bisa
- Menentukan apakah sebuah nilai masuk ke secret atau variable, di level mana, dan menebak nilai mana yang diterima job saat satu nama ada di dua level.
- Membaca secret dan variable di dalam job dengan aman: lewat with atau env, tidak pernah langsung di if:, dan tidak pernah di command line maupun di log.
- Menjelaskan apa yang dilindungi masking, aturan pull request dari fork, dan permission GITHUB_TOKEN, serta di mana batas masing-masing.
Ikuti diagramnya langkah demi langkah
Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.
Ke mana secret pergi, dan ke mana tidak
Secret dienkripsi di mesin Anda, disimpan di settings repository, hanya diberikan ke job yang menyebutnya, disamarkan di log run, ditulis ke server sebagai file yang hanya bisa dibaca pemiliknya, dan tidak diberikan ke pull request dari fork.
Tombol: ← → langkah · Home / End · K putar atau jeda
Anda menyimpan nilai di settings repository. deploy.yml menyebut apa yang dibutuhkan setiap job. Job berjalan di runner GitHub, menulis log run, dan menjangkau server lewat SSH.
gh secret set mengenkripsi nilai di mesin Anda sebelum dikirim. GitHub menyimpannya di Settings, Secrets and variables, Actions, dan API-nya tidak pernah mengembalikan nilai itu.
gh secret set VPS_SSH_KEYJob deploy menyebut secrets.VPS_SSH_KEY. GitHub mengevaluasi ekspresi itu dan memasukkan nilainya sebelum step berjalan. Secret yang tidak disebut job mana pun tidak pernah sampai ke runner.
key: secrets.VPS_SSH_KEYTebak langkah berikutnya Sebuah step meng-echo baris yang berisi key itu. Apa yang tampil di log run? (Key-nya, apa adanya · *** di tempat key itu · Tidak ada, step-nya diblokir)Runner mengganti setiap secret yang diterimanya dengan *** sebelum baris itu masuk log. Variable tercetak apa adanya. Salinan yang sudah diubah bentuknya bisa lolos: redaksi tidak dijamin.
Secret: ***Lewat SSH, script deploy menulis nilai yang sudah dirender ke .env, lalu chmod 600 membuat hanya pemilik file yang bisa membaca atau mengubahnya. Masking melindungi log; di server, permission yang melindungi.
chmod 600 .envTebak langkah berikutnya Misalkan workflow ini juga berjalan saat pull_request, lalu sebuah fork membukanya. Apa isi secrets.VPS_SSH_KEY di run itu? (Key yang sama, disamarkan · Salinan read-only · Nilai kosong)Pada pull_request dari fork, GitHub tidak memberikan secret apa pun kecuali GITHUB_TOKEN, dan token itu read-only. Ekspresinya bernilai kosong, jadi kode yang belum direview tidak pernah memegang key.
on: pull_request
Apa itu secrets dan variables di GitHub Actions?
Secret adalah nilai yang Anda simpan untuk workflow dan harus tetap rahasia: API key, SSH key, token. GitHub mengenkripsi secret dengan libsodium sealed box sebelum sampai ke GitHub, mendekripsinya untuk dimasukkan ke workflow run, dan workflow hanya bisa membaca secret yang disebutnya secara eksplisit. REST API untuk melihat daftar secret sebuah repository hanya mengembalikan nama dan waktu, tidak pernah nilainya.
Baca selengkapnya (1)Tutup
Configuration variable dipakai untuk sisanya: GitHub menyebut variable sebagai cara menyimpan dan memakai ulang konfigurasi yang tidak sensitif, dan mengingatkan bahwa nilainya tampil tanpa disamarkan di output build. Port, path deploy, atau saklar fitur cocok jadi variable. Keduanya dibaca lewat context: secrets.NAME untuk secret dan vars.NAME untuk variable, ditulis di dalam ekspresi sebagai ${{ secrets.NAME }} atau ${{ vars.NAME }}. Nama yang belum pernah diisi terbaca sebagai string kosong, untuk keduanya.
Bagaimana cara membuat secret atau variable?
Lewat browser: buka Settings repository, lalu Secrets and variables, lalu Actions. Tab Secrets punya tombol New repository secret, dan tab Variables punya New repository variable. Secret dan variable milik environment ada di Settings, Environments, di dalam masing-masing environment. Anda butuh akses write ke repository milik organization, atau menjadi collaborator di repository milik akun pribadi.
Baca selengkapnya (2)Tutup
Dari terminal, GitHub CLI melakukan hal yang sama. gh secret set NAME meminta nilai lewat prompt, --body mengambilnya dari argumen, dan redirect seperti gh secret set NAME < file membacanya dari standard input. CLI mengenkripsi nilainya secara lokal sebelum dikirim. Tambahkan --env NAME untuk environment atau --org NAME untuk organization, dan pakai gh variable set dengan flag yang sama untuk variable. gh secret list hanya menampilkan nama; gh variable list ikut menampilkan nilai.
Aturan nama sama untuk keduanya: hanya huruf, angka, dan underscore, tanpa spasi, tanpa awalan GITHUB_, dan tidak boleh diawali angka. Nama tidak membedakan huruf besar dan kecil saat dipanggil dan disimpan dalam huruf besar, jadi secrets.vps_ssh_key dan secrets.VPS_SSH_KEY adalah secret yang sama.
Bagaimana job sebaiknya membaca secret?
Berikan hanya ke step yang membutuhkannya, sebagai input action di bawah with: atau sebagai environment variable di bawah env:. GitHub mengevaluasi setiap ekspresi dan memasukkan hasilnya sebelum step berjalan, jadi secret yang ditulis langsung di script run: menjadi bagian dari teks script itu, dan GitHub mengingatkan bahwa secret sering berisi karakter khusus yang bisa mengganggu shell Anda. Taruh di env: dan beri tanda kutip pada variable shell-nya.
Baca selengkapnya (2)Tutup
Jauhkan secret dari command line. Saran GitHub adalah meneruskannya antar proses lewat environment variable, standard input, atau mekanisme lain yang didukung program itu, karena command line bisa terlihat oleh user lain atau tercatat di audit event. Karena itu docker login di sini menerima token lewat --password-stdin, bukan sebagai argumen.
Ada dua aturan yang sering mengejutkan. Context secrets tidak tersedia di kondisi if:, jadi salin secret ke env: di level job, lalu uji env.NAME di if: milik step. Dan secret tidak otomatis mengalir ke reusable workflow: pemanggilnya harus meneruskan secret lewat nama, atau dengan secrets: inherit di dalam organization atau enterprise yang sama.
Repository, environment, atau organization: nilai mana yang menang?
Secret dan variable sama-sama ada di tiga level. Milik organization dibagi ke banyak repository, dengan policy yang membatasi repository mana yang boleh memakai masing-masing. Milik repository melayani setiap workflow di repository itu. Milik environment terikat ke environment bernama, misalnya production, dan hanya job yang mereferensikan environment itu lewat environment: production yang bisa membacanya. Kalau satu nama ada di beberapa level, level paling rendah yang menang: environment mengalahkan repository, repository mengalahkan organization.
Baca selengkapnya (2)Tutup
Environment adalah tempat secret mendapat gerbang. Job yang mereferensikan sebuah environment harus lolos protection rule-nya sebelum berjalan atau membaca secret environment itu. Required reviewers berisi hingga enam orang atau tim, dan satu persetujuan sudah cukup untuk melanjutkan job. Wait timer menunda job, dan deployment branches and tags membatasi ref mana yang boleh deploy. Mereferensikan environment yang belum ada akan membuatnya, tanpa rule apa pun.
Di repository private, sebagian fitur environment bergantung pada paket GitHub Anda, jadi cek halaman environments sebelum merancang alur di atasnya. Ingat juga siapa yang tetap bisa menjangkau secret repository: panduan hardening GitHub menyatakan setiap user dengan akses write ke repository punya akses baca ke semua secret yang dikonfigurasi di sana.
Apa yang disembunyikan masking di log, dan apa yang terlewat?
Runner menyamarkan nilai setiap secret yang diterimanya di job saat itu, jadi baris yang seharusnya mencetak secret akan menampilkan *** sebagai gantinya. GitHub menegaskan ini tidak dijamin: secret bisa diubah bentuknya dengan banyak cara, dan runner hanya bisa menyamarkan secret yang dipakai di job saat itu. Variable tampil tanpa disamarkan.
Baca selengkapnya (1)Tutup
Tiga kebiasaan menjaga redaksi tetap bekerja. Simpan satu nilai per secret, jangan pernah gumpalan JSON, XML, atau YAML, karena data terstruktur bisa membuat redaksi gagal. Saat sebuah step menghasilkan nilai sensitif baru, misalnya token berumur pendek yang diambil memakai secret, daftarkan nilai itu dengan workflow command ::add-mask:: sebelum ada yang mencetaknya. Dan kalau sebuah nilai sempat masuk log tanpa disamarkan, hapus log itu dan rotasi secret-nya.
Apa yang didapat pull request dari fork dan GITHUB_TOKEN?
Saat pull request dari fork memicu workflow, GitHub tidak memberikan secret apa pun ke runner kecuali GITHUB_TOKEN, dan token itu read-only. Workflow yang dipicu Dependabot juga tidak mendapat secret. Di repository publik, run dari kontributor pertama kali secara default menunggu persetujuan maintainer. Jadi kode yang belum Anda review boleh menjalankan test, tetapi tidak pernah memegang key Anda.
Baca selengkapnya (2)Tutup
pull_request_target adalah pengecualiannya, sekaligus jebakannya. Event ini menjalankan workflow dari default branch repository dasar dengan token dan secret repository dasar, yang hanya aman selama kode tepercaya itu yang dijalankan. Kalau Anda checkout head pull request lalu menjalankan build-nya di sana, kode dari fork berjalan dengan secret Anda; GitHub menyebutnya pwn request. Pakai hanya kalau memang perlu, dan jangan pernah untuk mem-build kode fork.
GITHUB_TOKEN adalah satu-satunya secret yang tidak pernah Anda buat. GitHub membuat token baru di awal setiap job, terbatas pada repository itu, dan token kedaluwarsa saat job selesai, atau paling lama enam jam di runner yang di-host GitHub. Blok permissions:, di level workflow atau per job, menentukan apa yang boleh dilakukannya, dan setiap permission yang tidak Anda tulis diset ke none. Repository baru di akun pribadi defaultnya hanya punya akses baca untuk contents dan packages.
Konfigurasi nyata di balik pelajaran ini
Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.
# All app configuration is read from GitHub at run time:
# Secrets → GROQ_API_KEY, RESEND_API_KEY, VPS_SSH_HOST, VPS_SSH_USER, VPS_SSH_KEY
# Variables→ CONTACT_FROM_EMAIL, CONTACT_TO_EMAIL, APP_PORT, DEPLOY_PATH, VPS_SSH_PORTSepuluh nama, tanpa nilai: nilainya hanya ada di GitHub dan di server. Baca pembagiannya sebagai satu aturan: apa pun yang memberi akses, atau yang sebaiknya tidak muncul di log run, menjadi secret. Alamat dan user server memang bukan password, tetapi sebagai secret keduanya tampil sebagai *** di log, bukan tercetak apa adanya. Repository ini milik akun pribadi dan tidak ada job yang menyebut environment, jadi kesepuluhnya berada di level repository.
- name: Copy compose file to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_SSH_HOST }}
username: ${{ secrets.VPS_SSH_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ vars.VPS_SSH_PORT || 22 }}
source: docker-compose.yml
target: ${{ vars.DEPLOY_PATH }}Secret dikirim sebagai input action di bawah with:, salah satu dari dua cara yang ditunjukkan dokumentasi GitHub, jadi tidak pernah berada di perintah shell. Port dan path tujuan adalah variable. Variable yang belum diisi terbaca sebagai string kosong, dan vars.VPS_SSH_PORT || 22 ditulis agar memakai port 22 dalam keadaan itu.
cat > .env <<'ENVEOF'
GROQ_API_KEY=${{ secrets.GROQ_API_KEY }}
RESEND_API_KEY=${{ secrets.RESEND_API_KEY }}
CONTACT_FROM_EMAIL=${{ vars.CONTACT_FROM_EMAIL }}
CONTACT_TO_EMAIL=${{ vars.CONTACT_TO_EMAIL }}
IMAGE=${{ needs.build-and-push.outputs.image }}
APP_PORT=${{ vars.APP_PORT || 3002 }}
ENVEOF
chmod 600 .envGitHub merender setiap ekspresi sebelum script ini meninggalkan runner, jadi server menerima nilai aslinya. Masking hanya mengubah log GitHub; ia tidak berpengaruh apa pun pada file di mesin lain. Karena itu baris terakhir penting: chmod 600 membuat .env hanya bisa dibaca dan diubah oleh pemiliknya. Pelajaran 10 membahas cara Compose membaca file ini.
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdinGITHUB_TOKEN milik job deploy itu sendiri, yang dibatasi blok permissions:-nya ke contents: read dan packages: read, cukup untuk menarik image dan tidak lebih. Token dikirim ke docker login lewat standard input, sesuai saran GitHub, bukan sebagai argumen command line. Token kedaluwarsa saat job selesai, dan script menjalankan docker logout setelah container berjalan.
Kesalahan yang benar-benar membuang waktu
Secret kosong di dalam job
Ekspresi untuk secret yang tidak dimiliki run akan mengembalikan string kosong, jadi step tetap berjalan dengan nilai kosong. Cek berurutan: job berjalan dari pull request fork atau dari Dependabot, yang tidak mendapat secret; secret-nya milik environment tetapi job tidak punya key environment:; reusable workflow dipanggil tanpa meneruskan secret; atau namanya ditulis berbeda.
Kondisi yang membaca secret
if: secrets.DEPLOY_KEY != '' terlihat masuk akal, tetapi context secrets tidak tersedia di key if:. Jadikan secret itu entri env: di level job, lalu tulis kondisi step sebagai if: env.DEPLOY_KEY != '', pola yang didokumentasikan GitHub.
Secret tetap muncul di log
Redaksi hanya mencocokkan apa yang diketahui runner, jadi salinan yang diubah bentuknya, nilai di dalam gumpalan JSON, atau token turunan dari secret bisa tercetak apa adanya. Hapus log-nya dan rotasi secret-nya lebih dulu. Lalu simpan satu nilai per secret, daftarkan nilai turunan dengan ::add-mask::, dan berhenti mencetak nilainya sama sekali.
pull_request_target mem-build kode fork dengan secret Anda
Sebuah workflow diganti ke pull_request_target agar pull request dari fork bisa menjangkau secret, lalu checkout head pull request dan menjalankan npm install. Kode fork kini berjalan dengan token dan secret Anda. Tetap jalankan build kode fork di pull_request, dan pakai pull_request_target hanya untuk pekerjaan tepercaya seperti memberi label.
| Aspek | Secret | Configuration variable |
|---|---|---|
| Dipakai untuk | API key, SSH key, token: apa pun yang tidak akan Anda unggah ke publik | Konfigurasi tidak sensitif: port, path, saklar |
| Dibaca lewat | secrets.NAME, di with: atau env:, tidak di if: | vars.NAME, juga di if: dan environment: |
| Di log run | Tampil sebagai ***, tidak dijamin setelah diubah bentuknya | Tercetak apa adanya |
| Belum pernah diisi | String kosong | String kosong |
| Level | Organization, repository, environment | Organization, repository, environment |
| Nama sama di dua level | Level terendah menang: environment, lalu repository, lalu organization | Aturan yang sama |
| Level environment | Hanya job yang menyebut environment itu, setelah lolos protection rule | Hanya job yang menyebut environment itu |
| Batas | 100 per repository, 100 per environment, 48 KB per secret | 500 per repository, 100 per environment, 48 KB per variable |
Lakukan di komputer Anda sendiri
Pakai repository uji publik baru milik Anda sendiri dan nilai sekali pakai, jangan pernah key asli. Anda butuh GitHub CLI yang sudah login lewat gh auth login. Akun GitHub kedua, atau milik teman, hanya dibutuhkan untuk langkah fork.
Buat repository uji dengan README, supaya sudah punya branch main, lalu clone.
gh repo create secrets-lab --public --clone --add-readme cd secrets-labIsi satu secret dan satu variable. gh secret list menampilkan nama dan kapan diubah, tidak pernah nilainya; gh variable list ikut menampilkan nilainya.
gh secret set DEMO_SECRET --body "practice-only" gh variable set DEMO_GREETING --body "hello from a variable" gh secret list gh variable listTambahkan workflow yang membaca keduanya lewat env: di level job, menjalankan satu step hanya jika secret-nya sampai, dan menyamarkan nilai yang dibuatnya sendiri. Commit lalu push.
mkdir -p .github/workflows cat > .github/workflows/lab.yml <<'EOF' name: Secrets lab on: [push, pull_request] permissions: contents: read jobs: show: runs-on: ubuntu-latest env: DEMO_SECRET: ${{ secrets.DEMO_SECRET }} DEMO_GREETING: ${{ vars.DEMO_GREETING }} steps: - name: Print a variable and a secret run: | echo "Variable: $DEMO_GREETING" echo "Secret: $DEMO_SECRET" - name: Run only when the secret arrived if: env.DEMO_SECRET != '' run: echo "The secret reached this run" - name: Mask a value you made yourself run: | derived="lab-$RANDOM" echo "::add-mask::$derived" echo "Derived: $derived" EOF git add .github git commit -m "Add a secrets lab workflow" git pushTunggu beberapa detik sampai run dimulai, lalu pantau dan baca log-nya. Variable tercetak sesuai ketikan, sedangkan secret dan nilai turunan tampil sebagai ***.
run_id=$(gh run list --limit 1 --json databaseId --jq '.[0].databaseId') gh run watch "$run_id" gh run view "$run_id" --log | grep -E "Variable:|Secret:|Derived:|reached"Di browser, buka Settings, Environments, New environment. Beri nama lab, centang Required reviewers, tambahkan diri Anda, lalu simpan. Beri lab DEMO_SECRET miliknya sendiri dengan nilai yang lebih panjang, lalu tambahkan job yang menyebut environment itu. Run menunggu persetujuan Anda; setujui di halaman run, dan log mencetak Length 20, nilai milik environment, bukan 13.
gh secret set DEMO_SECRET --env lab --body "lab-environment-only" cat >> .github/workflows/lab.yml <<'EOF' gated: if: github.event_name == 'push' runs-on: ubuntu-latest environment: lab steps: - name: Which DEMO_SECRET won? env: DEMO_SECRET: ${{ secrets.DEMO_SECRET }} run: echo "Length ${#DEMO_SECRET}" EOF git commit -am "Add a gated job" git pushDari akun kedua, fork secrets-lab, ubah README.md di fork itu, lalu buka pull request. Kalau GitHub meminta, setujui run-nya: kontributor pertama kali secara default butuh persetujuan. Di run itu, Secret: tidak mencetak apa pun setelah titik dua, dan step yang membutuhkan secret dilewati.
Hapus repository uji. Penghapusan butuh scope delete_repo, dan --yes hanya melewati konfirmasi jika Anda menyebut repository-nya sebagai owner/name.
cd .. gh auth refresh -s delete_repo gh repo delete YOUR-USER/secrets-lab --yes
Tiga pertanyaan sebelum lanjut
Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada nilai, jadi tebak saja dengan bebas; jawaban yang salah disimpan di browser ini agar kursus bisa menanyakannya lagi nanti.
Dua pertanyaan dari pelajaran sebelumnya
Mengingat kembali pelajaran lama setelah jeda membuatnya lebih melekat. Jawab dari ingatan sebelum mencari apa pun.
Jawab dalam hati, lalu cek
1.VPS_SSH_HOST di situs ini bukan password. Kenapa dijadikan secret, bukan variable?Lihat jawabannya
Jawaban:Karena variable tercetak tanpa disamarkan, sedangkan secret disamarkan. Sebagai secret, alamat server tampil sebagai *** di setiap log run, bukan tertulis lengkap.
2.Bagaimana menjalankan sebuah step hanya jika secret-nya terisi?Lihat jawabannya
Jawaban:Context secrets tidak tersedia di if:. Salin secret ke entri env: di level job, lalu uji di kondisi step: if: env.NAME != ''.
3.Job deploy menulis contents: read dan packages: read. Apa yang boleh dilakukan GITHUB_TOKEN-nya terhadap issues?Lihat jawabannya
Jawaban:Tidak ada. Begitu blok permissions: menulis satu permission saja, setiap permission yang tidak ditulis diset ke none, jadi token itu hanya bisa membaca repository dan menarik package.
Bahasan mendalam terkait di situs ini
Sumber
Sumber primer yang diperiksa pada tanggal riset. Ikuti tautannya untuk versi terbaru.
- GitHub Docs: memakai secrets di GitHub Actions
- GitHub Docs: Secrets (enkripsi, penyebutan eksplisit, redaksi)
- GitHub Docs: referensi Secrets (nama, prioritas, batas)
- GitHub Docs: Variables (tidak sensitif, tidak disamarkan)
- GitHub Docs: menyimpan informasi di variables
- GitHub Docs: referensi Variables (nama, prioritas, batas)
- GitHub Docs: referensi Contexts (secrets, vars, ketersediaan)
- GitHub Docs: deployment dan environment (protection rule)
- GitHub Docs: mengelola environment untuk deployment
- GitHub Docs: referensi penggunaan aman (hardening)
- GitHub Docs: script injection (substitusi ekspresi)
- GitHub Docs: workflow command (add-mask)
- GitHub Docs: event yang memicu workflow (fork)
- GitHub Docs: memakai pull_request_target dengan aman
- GitHub Docs: GITHUB_TOKEN (pembuatan, masa berlaku)
- GitHub Docs: sintaks workflow (permissions)
- GitHub Docs: pengaturan Actions repository (default token, persetujuan fork)
- GitHub Docs: reuse workflow (meneruskan secret)
- GitHub Docs: endpoint REST API untuk Actions secrets
- Manual GitHub CLI: gh secret set
- Manual GitHub CLI: gh variable set