Cron ile SSL Sertifika Yenileme Otomasyonu

SSL sertifikası süresi dolmuş bir sunucuyla karşılaşmak, özellikle gece yarısı production ortamında alarm alırken, hiç kimsenin başına gelmesini istemediği türden bir durumdur. Müşteriler sitene giremiyor, tarayıcılar korkutucu uyarılar gösteriyor ve sen de aceleyle bir şeyler yapmaya çalışıyorsun. Oysa bu sorunun çözümü aslında çok basit: doğru kurgulanmış bir cron job ve birkaç satır script.

Bu yazıda Let’s Encrypt ve Certbot kullanarak SSL sertifika yenileme sürecini tamamen otomatize etmeyi, cron job’larla nasıl yöneteceğini, hata durumlarında nasıl bildirim alabileceğini ve production ortamında güvenli bir şekilde nasıl çalıştırabileceğini ele alacağız.

Neden Manuel Yenileme Yapmaya Devam Ediyorsun?

Let’s Encrypt sertifikaları 90 günde bir süresi doluyor. Bu süreyi bilinçli bir tercih olarak kısa tutuyorlar, çünkü kısa süreli sertifikalar güvenlik açısından daha iyi. Ama bu, her 90 günde bir birinin oturup “ah, sertifikayı yenilemem gerekiyor” demesi anlamına gelmiyor. Eğer hala takvime hatırlatıcı koyarak manuel yenileme yapıyorsan, bu işi yanlış yapıyorsun demektir.

Certbot zaten --renew mekanizmasına sahip ve sertifikanın süresinin dolmasına 30 günden az kaldığında yenileme yapıyor. Bizim yapacağımız şey bu mekanizmanın düzenli olarak tetiklenmesini sağlamak ve her adımda ne olduğunu kontrol altında tutmak.

Temel Altyapıyı Hazırlamak

Başlamadan önce sisteminde şunların kurulu olduğundan emin olalım:

# Ubuntu/Debian için
apt update && apt install -y certbot

# Nginx kullanıyorsan
apt install -y python3-certbot-nginx

# Apache kullanıyorsan
apt install -y python3-certbot-apache

# Kurulumu doğrula
certbot --version

Eğer daha önce manuel olarak sertifika almışsan, mevcut sertifikalarını görmek için:

certbot certificates

Bu komut sana tüm sertifikaların listesini, hangi domainler için geçerli olduklarını ve ne zaman sürelerinin dolacağını gösterir. Buradaki çıktıyı iyi incele, çünkü otomasyon scriptimizi bu yapı üzerine kuracağız.

Basit Bir Yenileme Scripti Yazmak

Doğrudan cron’a certbot renew komutu eklemek işe yarar ama hata yönetimi, loglama ve bildirim gibi kritik özellikleri kaçırırsın. Bunun yerine işi düzgün yapan bir script yazalım.

#!/bin/bash
# /usr/local/bin/ssl-renew.sh

set -euo pipefail

# Değişkenler
LOG_FILE="/var/log/ssl-renewal.log"
ALERT_EMAIL="[email protected]"
WEBHOOK_URL="https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
RENEWED=0
FAILED=0

# Log fonksiyonu
log() {
    echo "[$DATE] $1" | tee -a "$LOG_FILE"
}

# Bildirim fonksiyonu
notify() {
    local message="$1"
    local level="${2:-INFO}"
    
    # Email bildirimi
    if command -v mail &> /dev/null; then
        echo "$message" | mail -s "[SSL Yenileme - $level] $(hostname)" "$ALERT_EMAIL"
    fi
    
    # Slack webhook bildirimi
    if [ -n "$WEBHOOK_URL" ]; then
        curl -s -X POST -H 'Content-type: application/json' 
            --data "{"text":"[$level] $(hostname): $message"}" 
            "$WEBHOOK_URL" > /dev/null 2>&1
    fi
}

log "SSL yenileme süreci başlatıldı"

# Yenileme öncesi web sunucusu kontrolü
if ! systemctl is-active --quiet nginx; then
    log "UYARI: Nginx çalışmıyor!"
    notify "Nginx servisi aktif değil, yenileme atlandı" "WARNING"
    exit 1
fi

# Sertifikaları yenile
if certbot renew --quiet --post-hook "systemctl reload nginx" >> "$LOG_FILE" 2>&1; then
    RENEWED=1
    log "Sertifika yenileme başarıyla tamamlandı"
    notify "Sertifika yenileme başarıyla tamamlandı" "SUCCESS"
else
    FAILED=1
    log "HATA: Sertifika yenileme başarısız!"
    notify "Sertifika yenileme BAŞARISIZ! Acil kontrol gerekiyor." "CRITICAL"
    exit 1
fi

log "SSL yenileme süreci tamamlandı (Yenilendi: $RENEWED, Başarısız: $FAILED)"

Scripti oluşturduktan sonra çalıştırılabilir yapmalısın:

chmod +x /usr/local/bin/ssl-renew.sh

Cron Job’u Ayarlamak

Artık asıl konuya geldik. Cron job’u nasıl ayarlayacağız ve ne sıklıkla çalıştıracağız?

Let’s Encrypt’in resmi tavsiyesi günde iki kez çalıştırmak. Bu abartılı gibi görünse de sertifikanın süresine 30 günden az kalmışsa yenileme yapıldığı ve aksi halde hiçbir şey olmadığı düşünüldüğünde, günde iki kez çalıştırmak sistemin yükünü neredeyse hiç artırmıyor.

# Root crontab'ı düzenle
crontab -e -u root

Aşağıdaki satırı ekle:

# SSL Sertifika Yenileme - Her gün 03:00 ve 15:00'te çalışır
0 3,15 * * * /usr/local/bin/ssl-renew.sh >> /var/log/ssl-renewal-cron.log 2>&1

Neden 03:00 ve 15:00? Gece 3’te trafiğin en düşük olduğu saatte nginx reload yapıyoruz. 15:00 ise iş saatleri içinde ikinci bir kontrol fırsatı sunuyor. Let’s Encrypt sunucularının yoğun olduğu saatleri (tam saat başları ve yarımlar) da bu şekilde avoid etmiş oluyoruz.

Wildcard Sertifikalar için DNS Challenge

Eğer *.sirketin.com gibi wildcard bir sertifikan varsa, HTTP challenge yerine DNS challenge kullanman gerekiyor. Bu durumda otomasyon biraz daha karmaşıklaşıyor ama halledilebilir bir şey.

Cloudflare DNS kullanıyorsan:

# Cloudflare certbot eklentisini kur
apt install -y python3-certbot-dns-cloudflare

# Cloudflare kimlik bilgileri dosyasını oluştur
mkdir -p /etc/letsencrypt/cloudflare
cat > /etc/letsencrypt/cloudflare/credentials.ini << EOF
dns_cloudflare_api_token = CLOUDFLARE_API_TOKEN_BURAYA
EOF

chmod 600 /etc/letsencrypt/cloudflare/credentials.ini

Wildcard sertifika almak için:

certbot certonly 
    --dns-cloudflare 
    --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini 
    -d "sirketin.com" 
    -d "*.sirketin.com" 
    --preferred-challenges dns-01

Bu yöntemle yenileme de tamamen otomatik çalışır çünkü DNS kaydını da otomatik güncelliyor.

Çoklu Domain ve Sunucu Senaryosu

Gerçek dünyada genellikle tek bir sunucuda onlarca domain barındırılıyor ya da birden fazla sunucuda sertifika yönetimi yapılıyor. Bu senaryoyu ele alalım.

#!/bin/bash
# /usr/local/bin/ssl-bulk-check.sh
# Birden fazla domain için sertifika durumu kontrolü

DOMAINS=(
    "sirketin.com"
    "api.sirketin.com"
    "panel.sirketin.com"
    "blog.sirketin.com"
)

WARN_DAYS=30
CRITICAL_DAYS=14
ALERT_EMAIL="[email protected]"

check_cert_expiry() {
    local domain="$1"
    local expiry_date
    local days_left
    
    expiry_date=$(echo | openssl s_client -servername "$domain" 
        -connect "$domain:443" 2>/dev/null | 
        openssl x509 -noout -enddate 2>/dev/null | 
        cut -d= -f2)
    
    if [ -z "$expiry_date" ]; then
        echo "HATA: $domain için sertifika bilgisi alınamadı"
        return 1
    fi
    
    days_left=$(( ($(date -d "$expiry_date" +%s) - $(date +%s)) / 86400 ))
    
    if [ "$days_left" -lt "$CRITICAL_DAYS" ]; then
        echo "KRITIK: $domain sertifikası $days_left gün içinde sona eriyor!"
        echo "KRITIK: $domain - $days_left gün kaldı" | 
            mail -s "[KRITIK SSL] $domain" "$ALERT_EMAIL"
    elif [ "$days_left" -lt "$WARN_DAYS" ]; then
        echo "UYARI: $domain sertifikası $days_left gün içinde sona eriyor"
    else
        echo "OK: $domain sertifikası geçerli ($days_left gün kaldı)"
    fi
}

for domain in "${DOMAINS[@]}"; do
    check_cert_expiry "$domain"
done

Bu script doğrudan HTTPS bağlantısı kurarak sertifika durumunu kontrol ediyor. Certbot’un kendi kayıtlarına bakmak yerine gerçek dünya koşullarını test ettiği için daha güvenilir bir kontrol mekanizması sunuyor.

Nginx ile Post-Hook Entegrasyonu

Sertifika yenilendikten sonra web sunucusunu yeniden yüklemek kritik. Ama bunu yanlış yaparsanız, nginx bir hatayla reload edilemez ve siteniz çevrimdışı kalabilir.

Certbot’un hook sistemi burada devreye giriyor:

# /etc/letsencrypt/renewal-hooks/pre/check-nginx.sh
# Yenileme öncesinde nginx durumunu kontrol et

#!/bin/bash
if ! nginx -t 2>/dev/null; then
    echo "Nginx konfigürasyon hatası var, yenileme iptal ediliyor"
    exit 1
fi
# /etc/letsencrypt/renewal-hooks/post/reload-services.sh
# Yenileme sonrasında servisleri yeniden yükle

#!/bin/bash
LOG_FILE="/var/log/ssl-renewal.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')

echo "[$DATE] Post-hook tetiklendi, servisler yeniden yükleniyor" >> "$LOG_FILE"

# Nginx reload
if systemctl is-active --quiet nginx; then
    if systemctl reload nginx; then
        echo "[$DATE] Nginx başarıyla reload edildi" >> "$LOG_FILE"
    else
        echo "[$DATE] HATA: Nginx reload başarısız!" >> "$LOG_FILE"
        systemctl restart nginx
    fi
fi

# Eğer HAProxy da kullanıyorsan
if systemctl is-active --quiet haproxy; then
    # HAProxy için sertifika formatını birleştir
    cat /etc/letsencrypt/live/sirketin.com/fullchain.pem 
        /etc/letsencrypt/live/sirketin.com/privkey.pem 
        > /etc/haproxy/certs/sirketin.com.pem
    systemctl reload haproxy
    echo "[$DATE] HAProxy reload edildi" >> "$LOG_FILE"
fi

Bu hook dosyalarına çalıştırma izni vermeyi unutma:

chmod +x /etc/letsencrypt/renewal-hooks/pre/check-nginx.sh
chmod +x /etc/letsencrypt/renewal-hooks/post/reload-services.sh

Systemd Timer Alternatifi

Certbot zaten Ubuntu ve Debian sistemlerde kurulumla birlikte bir systemd timer oluşturuyor. Ama bunu özelleştirip daha iyi hale getirebilirsin. Bazı sysadminler cron yerine systemd timer tercih ediyor, çünkü systemd daha iyi loglama, bağımlılık yönetimi ve hata raporlama sunuyor.

Mevcut timer durumunu kontrol et:

systemctl status certbot.timer
systemctl list-timers | grep certbot

Eğer bu timer aktifse ve senin cron job’unla çakışacaksa, birini devre dışı bırakman gerekiyor:

# Certbot'un kendi timer'ını devre dışı bırak (kendi cron'unu kullanacaksan)
systemctl disable certbot.timer
systemctl stop certbot.timer

# Ya da kendi özel timer'ını oluştur
cat > /etc/systemd/system/ssl-renewal.service << EOF
[Unit]
Description=SSL Sertifika Yenileme Servisi
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/ssl-renew.sh
User=root
StandardOutput=journal
StandardError=journal
EOF

cat > /etc/systemd/system/ssl-renewal.timer << EOF
[Unit]
Description=SSL Sertifika Yenileme Zamanlayıcısı
Requires=ssl-renewal.service

[Timer]
OnCalendar=*-*-* 03,15:00:00
RandomizedDelaySec=3600
Persistent=true

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
systemctl enable ssl-renewal.timer
systemctl start ssl-renewal.timer

RandomizedDelaySec=3600 parametresi, birçok sunucunun aynı anda Let’s Encrypt API’sine istek göndermesini önlemek için rastgele bir gecikme ekliyor. Bu özellikle çok sayıda sunucu yönetiyorsan önemli.

Log Yönetimi ve İzleme

Otomasyon kurduğunda işin yarısı log yönetimi. Aylarca biriken logları yönetmek için logrotate kullanmalısın:

cat > /etc/logrotate.d/ssl-renewal << EOF
/var/log/ssl-renewal.log {
    weekly
    rotate 12
    compress
    delaycompress
    missingok
    notifempty
    create 640 root adm
}
EOF

Cron job’unun gerçekten çalışıp çalışmadığını kontrol etmek için:

# Son cron çalışmalarını görüntüle
grep ssl-renew /var/log/syslog | tail -20

# Yenileme loglarını takip et
tail -f /var/log/ssl-renewal.log

# Mevcut sertifika durumunu kontrol et
certbot certificates

# Dry-run ile yenileme simülasyonu yap
certbot renew --dry-run

--dry-run seçeneği gerçek bir yenileme yapmadan tüm süreci simüle eder. Yeni bir otomasyon kurduğunda her zaman önce bununla test et.

Gerçek Dünya Hatalarıyla Başa Çıkmak

Teoride her şey güzel görünüyor ama production’da işler nadiren teoride olduğu gibi gidiyor. Karşılaştığım yaygın sorunlar ve çözümleri:

Rate Limit Sorunu: Let’s Encrypt’in rate limitleri var. Aynı domain için haftalık 5 sertifika yenileme hakkın var. Bu limiti testler sırasında tüketebilirsin.

  • Çözüm: Test sırasında her zaman --dry-run kullan
  • Çözüm: Staging ortamı için --staging parametresi kullan
  • Çözüm: Cron job’unu yeterince seyrek ayarla (günde 2 kez yeterli)

Port 80 Engelli: HTTP challenge için port 80 açık olmalı. Güvenlik duvarı kuralları bunu engelliyor olabilir.

  • Çözüm: DNS challenge’a geç
  • Çözüm: Güvenlik duvarında certbot için geçici kural ekle

Nginx Reload Başarısız: Yenileme başarılı ama nginx düzgün reload edilemedi.

  • Çözüm: Post-hook’ta reload yerine nginx -t && systemctl reload nginx kullan
  • Çözüm: Reload başarısız olursa otomatik restart tetikle

Sertifika Yenilendi Ama Eski Sertifika Kullanılıyor: Nginx’in sertifikayı cache’lediği durumlar olabiliyor.

  • Çözüm: systemctl reload nginx değil systemctl restart nginx kullan (dikkatli ol, kısa kesinti olabilir)
  • Çözüm: Nginx konfigürasyonunda ssl_session_cache ayarlarını gözden geçir

Birden Fazla Sunucuyu Merkezi Yönetmek

Onlarca sunucu varsa, her birine gidip cron kurmanın mantıklı olmadığını söylemeden de geçemeyeceğim. Bu senaryoda tercih ettiğim yaklaşım sertifika yönetimini tek bir sunucuda toplamak ve diğer sunuculara dağıtmaktır.

#!/bin/bash
# /usr/local/bin/ssl-distribute.sh
# Sertifikaları diğer sunuculara dağıt

CERT_DIR="/etc/letsencrypt/live"
REMOTE_SERVERS=(
    "web1.sirketin.com"
    "web2.sirketin.com"
    "web3.sirketin.com"
)
REMOTE_USER="certdeploy"
REMOTE_CERT_DIR="/etc/ssl/certs"
SSH_KEY="/root/.ssh/certdeploy_rsa"

deploy_certs() {
    local server="$1"
    local domain="$2"
    
    echo "$(date): $server sunucusuna $domain sertifikası kopyalanıyor..."
    
    # Sertifika dosyalarını kopyala
    rsync -avz --delete 
        -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" 
        "$CERT_DIR/$domain/" 
        "$REMOTE_USER@$server:$REMOTE_CERT_DIR/$domain/"
    
    # Uzak sunucuda nginx'i reload et
    ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no 
        "$REMOTE_USER@$server" 
        "sudo systemctl reload nginx"
    
    echo "$(date): $server tamamlandı"
}

# Yenilenen sertifikaları bul ve dağıt
for domain_dir in "$CERT_DIR"/*/; do
    domain=$(basename "$domain_dir")
    cert_file="$domain_dir/cert.pem"
    
    # Son 24 saatte yenilendi mi kontrol et
    if [ -f "$cert_file" ] && 
       [ "$(find "$cert_file" -mtime -1 2>/dev/null)" ]; then
        echo "$(date): $domain yenilendi, dağıtım başlıyor..."
        for server in "${REMOTE_SERVERS[@]}"; do
            deploy_certs "$server" "$domain"
        done
    fi
done

Bu yaklaşım için uzak sunucularda certdeploy kullanıcısına nginx reload için sudo izni verilmesi gerekiyor. /etc/sudoers.d/certdeploy dosyasına şunu ekle:

certdeploy ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx

Sonuç

SSL sertifika yenileme otomasyonu, kurulduktan sonra neredeyse hiç dokunmadan çalışan, ama kurulmadığında hayatını zindan eden türden bir işlem. Bu yazıda anlattıklarını adım adım uygularsan, artık “sertifikan süresi doldu” konusunda gece yarısı alarm almayacaksın.

Özetlemek gerekirse kritik noktalar şunlar: İlk olarak, basit certbot renew komutunu cron’a eklemek yerine hata yönetimi ve bildirim mekanizması olan bir script yaz. İkinci olarak, günde iki kez çalıştır ama rastgele bir gecikme ekle, böylece Let’s Encrypt’i gereğinden fazla zorlamazsın. Üçüncü olarak, post-hook ile web sunucunun sertifikayı hemen yüklemesini sağla. Dördüncü olarak, her değişiklikten sonra --dry-run ile test et ve loglarını düzenli kontrol et. Son olarak, birden fazla sunucu yönetiyorsan merkezi bir dağıtım mekanizması kur.

En iyi otomasyon, farkında bile olmadığın zamanlarda sessizce çalışan otomasyondur. Bu sistemi bir kez kurduğunda, sertifika yenileme diye bir derdin kalmayacak ve enerjini daha önemli işlere harcayabileceksin.

Yorum yapın