Arşivleme İşlemlerinde Parola Yönetimi: gpg-agent ve pass ile Otomatik Şifre Girişi

Üretim ortamında çalışan bir sistem yöneticisinin en can sıkıcı anlarından biri şudur: bir cron job gece yarısı çalışıyor, şifreli bir arşiv oluşturmaya çalışıyor ve süreç parola beklediği için askıda kalıyor. Sabah geliyorsunuz, yedekleme başarısız, log’da “passphrase required but no tty” hatası. Bu yazıda bu problemi kökten çözecek bir yaklaşımdan bahsedeceğim: gpg-agent ve pass kullanarak arşivleme işlemlerinde otomatik şifre yönetimi.

Neden Parola Yönetimi Arşivlemenin Parçası Olmalı

Çoğu sysadmin arşivleme ve şifrelemeyi birbirinden bağımsız düşünür. tar ile sıkıştırırsın, gpg ile şifrelersin, işin biter. Ama otomasyona girdiğinizde bu iki adım arasındaki köprüyü kim kuruyor? Kimse. Ve bu boşluk güvenlik açıklarına zemin hazırlıyor.

Klasik çözümler şunlardır: şifreyi bir dosyaya yazıp --passphrase-file ile vermek, ya da environment variable’a gömmek. Her ikisi de berbat. Dosyaya yazılan parola düz metin olarak oturuyor, environment variable’lar ise ps aux ile görülebiliyor. Üretimde bu kabul edilemez.

gpg-agent + pass kombinasyonu bu problemi temiz bir şekilde çözüyor. pass, parolaları GPG ile şifrelenmiş olarak saklıyor. gpg-agent ise GPG işlemleri için parolayı önbelleğe alıyor ve belirli bir süre boyunca tekrar sormadan kullanıyor. Birlikte çalıştıklarında, scriptleriniz parola dosyası veya plain text içermeden şifreli arşivler oluşturabiliyor.

Temel Kurulum

Önce gerekli paketleri kuralım. Debian/Ubuntu tabanlı sistemler için:

sudo apt update
sudo apt install gnupg2 pass pinentry-curses -y

RHEL/CentOS/Rocky Linux için:

sudo dnf install gnupg2 pass pinentry -y

Kurulum sonrası GPG sürümünü kontrol edin:

gpg --version | head -3

GPG 2.1 ve üzeri olması önemli. Eski 1.x sürümlerinde gpg-agent entegrasyonu farklı çalışıyor ve burada anlattıklarım tam olarak uygulanamıyor.

GPG Anahtar Çifti Oluşturma

pass kullanabilmek için bir GPG anahtar çiftine ihtiyacınız var. Eğer halihazırda bir anahtarınız varsa bu adımı atlayabilirsiniz.

gpg --full-generate-key

Bu komut sizi interaktif bir sürece götürür. Anahtar tipi olarak RSA and RSA seçin, bit uzunluğu olarak 4096 tercih edin. Kullanım süresi için üretim ortamlarında sonsuz yerine 2 yıl gibi makul bir değer öneriyorum; anahtar rotasyonu güvenliğin parçası.

Oluşturduğunuz anahtarın ID’sini almak için:

gpg --list-secret-keys --keyid-format=long

Çıktı şöyle görünür:

sec   rsa4096/A1B2C3D4E5F6G7H8 2024-01-15 [SC]
      FINGERPRINT_BURAYA_GELIR
uid   [ultimate] Ahmet Yılmaz <[email protected]>
ssb   rsa4096/1234567890ABCDEF 2024-01-15 [E]

Burada A1B2C3D4E5F6G7H8 kısmı sizin anahtar ID’niz.

gpg-agent Konfigürasyonu

gpg-agent kurulumla birlikte geliyor ama varsayılan ayarları production senaryoları için optimize edilmiş değil. Konfigürasyon dosyasını düzenleyelim:

mkdir -p ~/.gnupg
cat > ~/.gnupg/gpg-agent.conf << 'EOF'
default-cache-ttl 3600
max-cache-ttl 86400
pinentry-program /usr/bin/pinentry-curses
allow-loopback-pinentry
EOF

chmod 700 ~/.gnupg
chmod 600 ~/.gnupg/gpg-agent.conf

Bu ayarları açıklayayım:

  • default-cache-ttl 3600: Son kullanımdan itibaren 1 saat parola önbellekte kalır
  • max-cache-ttl 86400: Parola maksimum 24 saat önbellekte kalır, kullanılmasa bile
  • pinentry-program: Headless sunucularda curses tabanlı pinentry kullanın
  • allow-loopback-pinentry: Scriptlerden parola göndermek için gerekli

Konfigürasyonu uygulamak için agent’ı yeniden başlatın:

gpgconf --kill gpg-agent
gpg-agent --daemon

pass Deposunu Başlatma

pass bir parola deposu (password store) yöneticisidir. Parolaları GPG anahtarınızla şifrelenmiş dosyalar olarak ~/.password-store/ altında saklar.

pass init A1B2C3D4E5F6G7H8

Buradaki ID’yi kendi anahtar ID’nizle değiştirin. Başarılı olursa şu mesajı görürsünüz:

Password store initialized for A1B2C3D4E5F6G7H8

Arşivleme işlemlerimiz için bir parola ekleyelim:

pass insert backup/arsiv-parolasi

Sistem iki kez parola isteyecek. Bu parola, şifreli arşivlerinizi koruyacak olan parola. Güçlü ve rastgele bir şey kullanın:

# Rastgele güçlü parola üretip doğrudan pass'e ekleyebilirsiniz
pass generate backup/arsiv-parolasi 32

Bu komut 32 karakterlik rastgele bir parola oluşturup backup/arsiv-parolasi yoluna kaydeder.

Arşivleme Scriptleri ile Entegrasyon

Şimdi asıl konuya gelelim. Aşağıdaki script, pass üzerinden parolayı alıp tar + gpg kombinasyonuyla şifreli arşiv oluşturuyor:

#!/bin/bash
# /usr/local/bin/sifreli-arsivle.sh

set -euo pipefail

KAYNAK_DIZIN="${1:?Kaynak dizin belirtilmeli}"
HEDEF_DOSYA="${2:?Hedef dosya yolu belirtilmeli}"
PASS_YOLU="backup/arsiv-parolasi"

# GPG agent socket'ini bul
export GPG_TTY=$(tty 2>/dev/null || echo "not-a-tty")
export GNUPGHOME="${GNUPGHOME:-$HOME/.gnupg}"

# Parolayı pass'den al
PAROLA=$(pass show "$PASS_YOLU")

if [ -z "$PAROLA" ]; then
    echo "HATA: Parola alınamadı" >&2
    exit 1
fi

# Sıkıştır ve şifrele
tar -czf - "$KAYNAK_DIZIN" | 
    gpg --batch 
        --yes 
        --passphrase "$PAROLA" 
        --symmetric 
        --cipher-algo AES256 
        --compress-algo none 
        -o "$HEDEF_DOSYA"

unset PAROLA
echo "Arşiv oluşturuldu: $HEDEF_DOSYA"

Scripti çalıştırılabilir yapın:

chmod 750 /usr/local/bin/sifreli-arsivle.sh

Test edelim:

sifreli-arsivle.sh /etc/nginx /tmp/nginx-yedek-$(date +%Y%m%d).tar.gz.gpg

Asimetrik Şifreleme ile Daha Güvenli Yaklaşım

Simetrik şifreleme (--symmetric) kolay ama daha güvenli bir yaklaşım asimetrik şifredir. Bu durumda parola yerine GPG anahtarınızı kullanırsınız:

#!/bin/bash
# /usr/local/bin/gpg-arsivle.sh

set -euo pipefail

KAYNAK="${1:?Kaynak belirtilmeli}"
HEDEF="${2:?Hedef belirtilmeli}"
ALICI_KEY="${3:-A1B2C3D4E5F6G7H8}"

tar -czf - "$KAYNAK" | 
    gpg --batch 
        --yes 
        --trust-model always 
        --encrypt 
        --recipient "$ALICI_KEY" 
        -o "$HEDEF"

echo "Şifreli arşiv hazır: $HEDEF"

Asimetrik şifrelemede gpg-agent parolayı önbelleğe aldığı sürece herhangi bir pin girişi gerekmez. Bu yaklaşımda pass‘e ihtiyacınız olmadığını fark edeceksiniz; gpg-agent‘ın önbelleği tek başına yeterli.

Şifreyi çözmek için:

gpg --batch --yes --decrypt /tmp/nginx-yedek-20240115.tar.gz.gpg | tar -xzf - -C /tmp/restore/

Cron Job Entegrasyonu

Cron’da çalışacak bir arşivleme görevi için birkaç şeyi düzgün ayarlamak gerekiyor. Cron ortamında GPG_AGENT_INFO veya socket bilgisi olmayabilir:

#!/bin/bash
# /usr/local/bin/gece-yedek.sh

set -euo pipefail

LOG_DOSYA="/var/log/yedekleme.log"
TARIH=$(date +%Y%m%d-%H%M%S)

# Log fonksiyonu
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_DOSYA"
}

# GPG agent socket'ini bul veya başlat
if ! pgrep -u "$USER" gpg-agent > /dev/null 2>&1; then
    eval $(gpg-agent --daemon --options ~/.gnupg/gpg-agent.conf 2>/dev/null)
    log "GPG agent başlatıldı"
fi

# Agent socket'ini ortam değişkenine bağla
GPG_AGENT_SOCK=$(gpgconf --list-dirs agent-socket 2>/dev/null)
export GPG_AGENT_INFO="${GPG_AGENT_SOCK}:0:1"

# Yedekleme dizinleri
YEDEK_DIZINLER=(
    "/etc"
    "/var/www/html"
    "/home"
)

HEDEF_KLASS="/backup/gunluk"
mkdir -p "$HEDEF_KLASS"

for DIZIN in "${YEDEK_DIZINLER[@]}"; do
    DIZIN_ADI=$(basename "$DIZIN")
    HEDEF_DOSYA="${HEDEF_KLASS}/${DIZIN_ADI}-${TARIH}.tar.gz.gpg"
    
    log "Yedekleniyor: $DIZIN"
    
    if tar -czf - "$DIZIN" 2>/dev/null | 
        gpg --batch 
            --yes 
            --passphrase "$(pass show backup/arsiv-parolasi)" 
            --symmetric 
            --cipher-algo AES256 
            --compress-algo none 
            -o "$HEDEF_DOSYA"; then
        log "Başarılı: $HEDEF_DOSYA"
    else
        log "HATA: $DIZIN yedeklenemedi"
    fi
done

# 30 günden eski yedekleri temizle
find "$HEDEF_KLASS" -name "*.tar.gz.gpg" -mtime +30 -delete
log "Eski yedekler temizlendi"

Crontab’a ekleyin:

crontab -e
0 2 * * * /usr/local/bin/gece-yedek.sh

Preseed Yaklaşımı ile gpg-agent Önbelleğini Doldurma

Bazı senaryolarda, özellikle uzun süre çalışacak otomasyonlarda, agent’ın önbelleğinin dolu olduğundan emin olmak isteyebilirsiniz. Bunun için bir preseed scripti yazabilirsiniz:

#!/bin/bash
# /usr/local/bin/gpg-preseed.sh
# Bu script agent önbelleğini önceden doldurur

PASS_YOLU="backup/arsiv-parolasi"

# Agent'ın çalıştığından emin ol
gpgconf --launch gpg-agent

# Küçük bir test şifrelemesi yaparak önbelleği aktifleştir
echo "test" | 
    gpg --batch 
        --yes 
        --passphrase "$(pass show $PASS_YOLU)" 
        --symmetric 
        --cipher-algo AES256 
        -o /dev/null 2>/dev/null

echo "GPG agent önbelleği hazır"

Bu scripti büyük yedekleme işleminden önce çalıştırarak agent’ın uyanık olduğundan emin olabilirsiniz.

Systemd ile Entegrasyon

Modern Linux sistemlerinde cron yerine systemd timer kullanmak daha iyi bir yaklaşım. Log yönetimi, bağımlılıklar ve başarısızlık yönetimi çok daha temiz:

# /etc/systemd/system/gece-yedek.service
cat > /etc/systemd/system/gece-yedek.service << 'EOF'
[Unit]
Description=Gece Şifreli Yedekleme
After=network.target
Wants=gpg-agent.service

[Service]
Type=oneshot
User=backup-user
Environment="GNUPGHOME=/home/backup-user/.gnupg"
ExecStartPre=/usr/local/bin/gpg-preseed.sh
ExecStart=/usr/local/bin/gece-yedek.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=gece-yedek

[Install]
WantedBy=multi-user.target
EOF

# /etc/systemd/system/gece-yedek.timer
cat > /etc/systemd/system/gece-yedek.timer << 'EOF'
[Unit]
Description=Gece Şifreli Yedekleme Zamanlayıcısı

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
systemctl enable --now gece-yedek.timer

Sorun Giderme

Gerçek hayatta en sık karşılaşılan sorunlar ve çözümleri:

“gpg: cannot open tty” hatası: Headless ortamlarda --batch flag’ini ekleyin ve GPG_TTY değişkenini ayarlayın.

export GPG_TTY=/dev/null
gpg --batch --passphrase-fd 0 ...

Agent bulunamıyor: Socket’i manuel olarak kontrol edin.

gpgconf --list-dirs agent-socket
ls -la $(gpgconf --list-dirs agent-socket)

pass komutu “No such file” diyor: pass deposunun başlatıldığından ve doğru anahtarla şifrelendiğinden emin olun.

pass ls
# Depo listesini gösterir, boşsa init atmışsınızdır ama parola eklememiş olabilirsiniz

Cron’da “no pinentry” hatası: gpg-agent.conf dosyasında allow-loopback-pinentry ve pinentry-program /usr/bin/pinentry-curses satırlarının olduğundan emin olun.

Güvenlik Notları

Bu kurulumu production’a alırken dikkat etmeniz gereken birkaç nokta:

  • pass deposunu yedekleyin: ~/.password-store/ dizinini GPG anahtarınızla birlikte güvenli bir yerde saklayın. Anahtar giderse parolalar gider.
  • Anahtar parola kalitesi: GPG anahtarınızın kendisini koruyan parolayı güçlü tutun. gpg-agent bu parolayı önbelleğe alıyor ama fiziksel anahtar güvende olmalı.
  • chmod kontrolü: Script dosyalarınız ve .gnupg dizininiz düzgün izinlere sahip olsun. 700 dizin, 600 konfigürasyon dosyaları.
  • Anahtar rotasyonu: GPG anahtarlarınız için bir rotasyon planı yapın. Eski anahtarla şifrelenmiş arşivleri yeni anahtarla yeniden şifrelemeyi unutmayın.
  • Log’larda parola bırakmayın: Scriptlerinizde set -x debug modunu prod’da kapalı tutun; parola değişkenlerini terminale yazdırmayın.

Sonuç

gpg-agent ve pass kombinasyonu, arşivleme iş akışlarınıza temiz ve güvenli bir parola yönetimi katmanı ekliyor. Plain text parola dosyalarından, environment variable’lara gömülü sırlardan kurtuluyorsunuz. Otomasyon güvenli oluyor, log’larda hassas veri kalmıyor.

Bu yaklaşımın en büyük avantajı ölçeklenebilirlik. Tek bir sunucuda çalışan bir script için overengineering gibi görünebilir ama 20 sunucuda çalışan bir yedekleme altyapısı kurduğunuzda, her makinede tek bir GPG anahtar çiftiyle tüm süreci kontrol edebilmek büyük rahatlık sağlıyor. Vault veya başka gizli yönetim araçlarına geçmeden önce bu kombinas yon çoğu orta ölçekli yapı için fazlasıyla yeterli.

Bir sonraki adım olarak pass deposunu Git ile versiyonlamayı inceleyebilirsiniz; pass git init ile başlayan bu süreç, parola geçmişi ve ekip paylaşımı konularında da kapılar açıyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir