GitHub Actions’da Secrets Yönetimi: Güvenli Değişkenler Nasıl Kullanılır?
Production ortamında bir pipeline’ın çökmesi ve ardından logları incelerken API anahtarının düz metin olarak commit geçmişinde durduğunu fark etmek… Bu senaryoyu yaşayan herkes biliyor, o soğuk ter anını. GitHub Actions’ta secrets yönetimi, sadece “değişkenleri nereye koyacağım” sorusunun çok ötesinde bir konu. Yanlış yapılandırılmış bir pipeline, production veritabanı şifrenizi, cloud provider credential’larınızı veya third-party API anahtarlarınızı açık havaya bırakabilir. Bu yazıda, GitHub Actions’ta güvenli değişken yönetimini baştan sona ele alacağız; hem teorik hem de sahadan örneklerle.
Secrets ve Variables Arasındaki Fark
GitHub Actions, iki farklı tip gizli bilgi yönetimi sunar ve bu ikisini karıştırmak ciddi güvenlik açıklarına yol açar.
Secrets (Gizli Bilgiler): Şifreler, API anahtarları, private key’ler gibi hassas veriler. Bir kez kaydedildikten sonra UI üzerinden görüntülenemez, sadece üzerine yazılabilir. Workflow loglarında otomatik olarak maskelenir.
Variables (Değişkenler): Ortam adları, konfigürasyon değerleri gibi hassas olmayan veriler. Değeri okunabilir, UI’da görüntülenebilir. Maskeleme yoktur.
Pratikte şunu yapın: Production URL’inizi variable olarak saklayın, ama o URL’e bağlanmak için kullanacağınız şifreyi kesinlikle secret olarak saklayın.
Secret Seviyeleri ve Kapsamları
GitHub Actions’ta üç farklı seviyede secret tanımlayabilirsiniz:
Repository Secrets: Sadece ilgili repository’nin workflow’larında kullanılabilir. En kısıtlı ve güvenli seçenek.
Environment Secrets: Belirli bir deployment environment’ına (staging, production gibi) özgü secretlar. Environment protection rules ile birleşince çok güçlü bir yapı oluşturur.
Organization Secrets: Tüm organizasyon repository’lerinde kullanılabilir. Paylaşılan credential’lar için ideal, ama kapsam yönetimine dikkat etmek gerekir.
Repository secrets’a ulaşmak için: Settings > Secrets and variables > Actions > New repository secret
Temel Secret Kullanımı
En basit haliyle bir secret’ı workflow’da nasıl kullanırsınız:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy application
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.THIRD_PARTY_API_KEY }}
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
echo "Deployment basliyor..."
./scripts/deploy.sh
Burada dikkat edilmesi gereken birkaç nokta var. ${{ secrets.SECRET_NAME }} sözdizimini kullanıyorsunuz ve bu değeri doğrudan run bloğuna değil, env bloğuna geçiriyorsunuz. Neden? Çünkü doğrudan run içinde echo ${{ secrets.MY_SECRET }} yaparsanız, GitHub maskeleme işlemi yapsa da bu pratik bir güvenlik açığı yaratır.
Environment’a Özgü Secret Yönetimi
Gerçek dünya senaryolarında staging ve production için farklı credential’larınız olur. Bunu environment secrets ile yönetelim:
name: Multi-Environment Deployment
on:
push:
branches:
- main
- staging
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
if: github.ref == 'refs/heads/staging'
steps:
- uses: actions/checkout@v4
- name: Deploy to Staging
env:
DB_HOST: ${{ vars.DB_HOST }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
./deploy.sh --env staging
deploy-production:
runs-on: ubuntu-latest
environment: production
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Production
env:
DB_HOST: ${{ vars.DB_HOST }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
./deploy.sh --env production
Burada aynı secret adlarını (DB_PASSWORD, AWS_ACCESS_KEY_ID) kullandığınıza dikkat edin, ama her environment altında farklı değerler tanımlıyorsunuz. staging environment’ında staging DB şifresi, production environment’ında production DB şifresi bulunuyor. Workflow kodu aynı kalıyor, sadece environment bağlamı değişiyor.
Environment Protection Rules ile Güvenliği Artırma
Environment secrets’ın asıl gücü, protection rules ile ortaya çıkar. Settings > Environments > production yolundan şunları yapılandırabilirsiniz:
- Required reviewers: Production deploy’u tetiklemeden önce belirli kişilerin onayı gereksin.
- Wait timer: Deploy’dan önce X dakika beklensin.
- Deployment branches: Sadece
mainbranch’ten deploy mümkün olsun.
Bu sayede birisi yanlışlıkla production secret’larını kullanmaya çalışırsa, workflow otomatik olarak onay bekleme durumuna geçer.
AWS Credential Yönetimi: OIDC ile Anahtarsız Yaklaşım
Bu kısım, çoğu sysadmin’in gözden kaçırdığı ve çok kritik olan bir konudur. AWS erişimi için statik access key/secret key çifti saklamak yerine, OIDC (OpenID Connect) kullanarak anahtarsız kimlik doğrulaması yapabilirsiniz.
Neden OIDC? Statik anahtarlar döner, sızabilir, expire olabilir. OIDC ile GitHub Actions, her workflow çalıştığında geçici token alır. Bu token workflow bittikten sonra geçersizdir.
Önce AWS tarafında IAM Identity Provider oluşturun:
# AWS CLI ile OIDC Provider olusturma
aws iam create-open-id-connect-provider
--url https://token.actions.githubusercontent.com
--client-id-list sts.amazonaws.com
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
# IAM Role olusturma (trust policy)
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:GITHUB_ORG/REPO_NAME:*"
}
}
}
]
}
EOF
aws iam create-role
--role-name GitHubActionsRole
--assume-role-policy-document file://trust-policy.json
Sonra workflow’da bu role’ü kullanın:
name: Deploy with OIDC
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: eu-west-1
- name: Deploy to S3
run: |
aws s3 sync ./dist s3://my-production-bucket
aws cloudfront create-invalidation
--distribution-id ${{ vars.CF_DISTRIBUTION_ID }}
--paths "/*"
Dikkat: permissions bloğu kritik. id-token: write olmadan OIDC token alınamaz.
Docker Registry ile Güvenli Image Push
Birçok pipeline, Docker image build edip registry’ye push eder. Bu süreçte credential yönetimi:
name: Build and Push Docker Image
on:
push:
branches: [main]
tags: ['v*.*.*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myorg/myapp:latest
myorg/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.run_id }}
GIT_COMMIT=${{ github.sha }}
Burada DOCKERHUB_TOKEN olarak şifrenizi değil, DockerHub’dan oluşturduğunuz access token’ı kullanın. Şifre değiştiğinde tüm token’ları sıfırlamak zorunda kalmazsınız, sadece ilgili token’ı iptal edersiniz.
Secret Maskeleme ve Güvenli Loglama
GitHub Actions, bilinen secret değerlerini otomatik olarak maskeler. Ama bu maskelemenin sınırları var:
name: Debug-Safe Logging
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Guvenli degisken kullanimi
env:
API_KEY: ${{ secrets.API_KEY }}
DB_CONN: ${{ secrets.DATABASE_CONNECTION_STRING }}
run: |
# YANLIS: Secret degerini echo etmek
# echo "API Key: $API_KEY" -- BUNU YAPMAYIN
# DOGRU: Sadece var mi yok mu kontrol et
if [ -z "$API_KEY" ]; then
echo "HATA: API_KEY secret tanimlanmamis!"
exit 1
else
echo "API_KEY mevcut ve dolu."
fi
# Connection string'den hassas kisimlari cikarmadan loglama
# YANLIS: echo $DB_CONN
# DOGRU:
DB_HOST=$(echo $DB_CONN | cut -d'@' -f2 | cut -d'/' -f1)
echo "Veritabanina baglaniliyor: $DB_HOST"
Base64 encoded secret’lar maskelenmez. Eğer bir secret’ı base64 ile encode edip log’a basarsanız, GitHub bunu maskeleyemez. Bu yüzden decode edilmiş hallerde dikkatli olun.
Dinamik Secret Yönetimi: HashiCorp Vault Entegrasyonu
Büyük ölçekli yapılarda, GitHub’ın kendi secret yönetiminin yetmediği durumlar olur. HashiCorp Vault gibi external secret manager’larla entegrasyon kurmak mümkün:
name: Vault Integration
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Import Secrets from Vault
uses: hashicorp/vault-action@v3
with:
url: ${{ secrets.VAULT_ADDR }}
token: ${{ secrets.VAULT_TOKEN }}
secrets: |
secret/data/myapp/production db_password | DB_PASSWORD ;
secret/data/myapp/production api_key | EXTERNAL_API_KEY ;
secret/data/aws/creds/deploy access_key | AWS_ACCESS_KEY_ID ;
secret/data/aws/creds/deploy secret_key | AWS_SECRET_ACCESS_KEY
- name: Use imported secrets
run: |
# Bu noktada DB_PASSWORD, EXTERNAL_API_KEY vb.
# environment variable olarak kullanilabilir
./deploy.sh
Bu yaklaşımda GitHub sadece Vault’a erişim için kullanılan VAULT_TOKEN‘ı tutar. Uygulama secret’larının tamamı Vault’ta merkezi olarak yönetilir. Secret rotation yaptığınızda GitHub tarafında hiçbir şeyi güncellemeniz gerekmez.
Ortak Hatalar ve Çözümleri
Sahada en çok rastladığım hataları ve çözümlerini derledim:
Hata 1: Secret’ı doğrudan script içine gömmek
# YANLIS - Bunu workflow'unuzda gorursek ciddi konusmaliyiz
- name: Bad practice
run: |
curl -H "Authorization: Bearer ${{ secrets.API_KEY }}"
https://api.example.com/deploy
# Bu yaklaşim workflow YAML'ini loglar ve guvensizdir
# DOGRU
- name: Good practice
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
curl -H "Authorization: Bearer $API_KEY"
https://api.example.com/deploy
Hata 2: Fork’lardan gelen Pull Request’lerde secret erişimi
Fork’lardan açılan PR’larda, güvenlik nedeniyle secret’lar workflow’a verilmez. Bu genellikle CI testlerini kırar. Çözüm:
name: PR Tests
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
env:
# Fork PR'larinda bu bos gelir, test bunu handle etmeli
TEST_API_KEY: ${{ secrets.TEST_API_KEY }}
run: |
if [ -z "$TEST_API_KEY" ]; then
echo "Fork PR: External API testleri atlanıyor"
npm test -- --skip-integration
else
npm test
fi
Hata 3: Secret adlandırma tutarsızlığı
Organizasyonlarda yaygın bir sorun: biri AWS_KEY, diğeri AWS_ACCESS_KEY, başkası AWS_ACCESS_KEY_ID kullanıyor. Standart bir isimlendirme politikası belirleyin ve bunu dokümante edin.
Secret Rotation Stratejisi
Secret’ları düzenli döndürmek (rotate) iyi bir güvenlik pratiğidir. Bunu otomatize edebilirsiniz:
name: Rotate API Secrets
on:
schedule:
# Her ay 1'inde calis
- cron: '0 0 1 * *'
workflow_dispatch:
jobs:
rotate:
runs-on: ubuntu-latest
steps:
- name: Generate new API key
env:
CURRENT_KEY: ${{ secrets.THIRD_PARTY_API_KEY }}
SERVICE_ADMIN_TOKEN: ${{ secrets.SERVICE_ADMIN_TOKEN }}
run: |
# Yeni key olustur
NEW_KEY=$(curl -s -X POST
-H "Authorization: Bearer $SERVICE_ADMIN_TOKEN"
https://api.thirdparty.com/v1/keys
| jq -r '.key')
# Yeni key'i GitHub'a yaz
gh secret set THIRD_PARTY_API_KEY --body "$NEW_KEY"
# Eski key'i iptal et
curl -s -X DELETE
-H "Authorization: Bearer $SERVICE_ADMIN_TOKEN"
-H "X-Old-Key: $CURRENT_KEY"
https://api.thirdparty.com/v1/keys/current
echo "Secret rotation tamamlandi"
env:
GH_TOKEN: ${{ secrets.PAT_FOR_SECRET_MANAGEMENT }}
Audit ve Monitoring
Secret kullanımını izlemek de en az secret tanımlamak kadar önemli:
- GitHub Audit Log: Organization ayarlarından secret erişimlerini izleyebilirsiniz.
Settings > Audit logbölümündensecretkeyword’ü ile filtreleyebilirsiniz.
- Secret Scanning: GitHub’ın yerleşik secret scanning özelliği, repository’e yanlışlıkla commit edilen secret pattern’larını tespit eder.
Settings > Security > Secret scanningkısmından aktif edin.
- Workflow run logs: Başarısız deploymentlarda, log’larda secret değerlerini aramak yerine workflow’un hangi adımda hata aldığını takip edin.
Sonuç
GitHub Actions’ta secrets yönetimi, pipeline güvenliğinin temel taşıdır. Özet olarak uygulamanız gereken pratikler:
- Repository, environment ve organization secret seviyelerini doğru kullanın, her şeyi aynı sepete koymayın.
- AWS gibi cloud provider’lar için statik anahtarlar yerine OIDC ile geçici credential kullanın.
- External vault entegrasyonu kurarak secret’ları merkezi yönetin ve rotation’ı otomatize edin.
- Fork PR senaryolarını test akışlarınızda hesaba katın, secret olmadan da çalışan graceful fallback’ler yazın.
- Secret scanning’i aktif tutun ve audit logları periyodik inceleyin.
envbloğu kullanımını alışkanlık haline getirin, secret değerlerini asla doğrudanruniçine enjekte etmeyin.
Production’da bir secret sızdığında farkında olmak bazen günler sürebilir. Bu pratikleri baştan doğru uygulamak, sonradan olası bir breach’i handle etmekten kat kat kolaydır. Pipeline’larınız güvenli, deploymentlarınız sorunsuz olsun.
