Let’s Encrypt Sertifikası Süresi Doluyor: Adım Adım Çözüm Rehberi
Sabah 07:30’da gelen o e-posta bildirimini hepiniz tanırsınız: “Your certificate will expire in 14 days.” Sonra bir hafta daha geçer, bu sefer “7 days.” Ve siz hala “hallederim” diyerek işin içinden çıkmazsınız. Derken bir Pazartesi sabahı kullanıcılardan “site açılmıyor” mesajları gelmeye başlar. İşte bu yazı tam olarak o an için.
Let’s Encrypt sertifikası yenileme sorunları, görünürde basit ama pratikte can sıkıcı bir konu. Otomatik yenileme neden çalışmadı? Manuel nasıl yapılır? Wildcard sertifikayla durum farklı mı? Hepsini gerçek senaryolar üzerinden ele alacağız.
Önce Durumu Tespit Et
Panik yapmadan önce mevcut durumu net olarak ortaya koy. Sertifikanın tam olarak ne zaman dolacağını ve hangi domainleri kapsadığını bilmek, sonraki adımları planlamak için şart.
# Mevcut sertifika bilgilerini göster
certbot certificates
# Belirli bir domain için SSL bilgisini doğrudan kontrol et
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
# Sertifika dosyasını doğrudan oku
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -text | grep -A2 "Validity"
certbot certificates çıktısında şuna benzer bir şey görürsün:
Found the following certs:
Certificate Name: example.com
Domains: example.com www.example.com
Expiry Date: 2024-02-15 10:23:00+00:00 (VALID: 3 days)
Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
“VALID: 3 days” görüyorsan artık teorik tartışma zamanı değil, aksiyon zamanı.
Otomatik Yenileme Neden Çalışmadı?
Bu soruyu sormadan geçmemek lazım. Çünkü manuel olarak yenileyip geçmek sorunu çözmez, sadece ertelenmiş bir krizi yaratır.
Certbot kurulumunda genellikle iki mekanizmadan biri devreye girer: systemd timer veya cron job. Hangisinin aktif olduğunu kontrol et:
# Systemd timer kontrolü
systemctl status certbot.timer
systemctl list-timers | grep certbot
# Cron job kontrolü
crontab -l
cat /etc/cron.d/certbot
ls /etc/cron.daily/ | grep certbot
Eğer systemd timer varsa ve aktif görünüyorsa, son çalışmasına bak:
journalctl -u certbot.service --since "30 days ago" | tail -50
Buradan sık karşılaşılan hata mesajlarına bakarak sorunun kaynağını bulabilirsin. En yaygın sebepler şunlar:
- Port 80 kapalı veya başka bir servis işgal etmiş: HTTP-01 challenge için 80. port boş olmalı
- DNS değişikliği: Domain farklı bir IP’ye yönlendiriliyor, sunucu bunu beklemiyor
- Web sunucusu konfigürasyon hatası:
.well-known/acme-challengedizinine erişim engellenmiş - Rate limit: Aynı domain için kısa sürede çok fazla istek yapılmış
- Firewall kuralı: Dış dünyadan 80 veya 443’e erişim kapalı
Manuel Yenileme: Temel Yöntem
Durumu anladıktan sonra önce manuel yenilemeyi çalıştır, ardından otomatik yenilemeyi düzelt.
# Kuru çalıştırma - gerçekte sertifika almaz, sadece test eder
certbot renew --dry-run
# Gerçek yenileme
certbot renew
# Sadece belirli bir domaini yenile
certbot renew --cert-name example.com
# Verbose modda çalıştır, ne olduğunu görmek için
certbot renew --cert-name example.com -v
--dry-run çıktısında hata alırsan asıl sorunu orada göreceksin. Örneğin şöyle bir hata:
Attempting to renew cert (example.com) from /etc/letsencrypt/renewal/example.com.conf
produced an unexpected error: Problem binding to port 80: Could not bind to IPv4 or IPv6.
Bu mesaj, 80. portun kullanımda olduğunu söylüyor. Nginx veya Apache çalışıyorsa ve standalone plugin kullanıyorsan çakışma olur. Bu durumda webroot veya nginx/apache plugin’ini kullanman gerekir.
Challenge Türüne Göre Yenileme Yöntemleri
Kurulum sırasında hangi challenge yöntemini kullandığın önemli. Yenileme de aynı yöntemle yapılmak zorunda değil, değiştirebilirsin.
HTTP-01 Challenge ile Webroot
Web sunucusu çalışıyorken sertifika yenilemek için en temiz yöntem:
# Nginx çalışırken webroot ile yenile
certbot renew --webroot -w /var/www/html --cert-name example.com
# Apache için
certbot renew --apache --cert-name example.com
# Nginx için
certbot renew --nginx --cert-name example.com
Webroot yöntemi çalışmıyorsa, Nginx’in .well-known dizinine doğru şekilde servis ettiğinden emin ol:
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/html;
allow all;
}
location / {
return 301 https://$host$request_uri;
}
}
Standalone Yöntem
Eğer web sunucusunu durdurmak sorun değilse veya sadece sertifika yenileme sırasında kısa bir kesinti kabul edilebiliyorsa:
# Nginx'i durdur
systemctl stop nginx
# Sertifikayı yenile
certbot renew --standalone --cert-name example.com
# Nginx'i tekrar başlat
systemctl start nginx
Prodüksiyonda bu yöntemi kullanmak için önceden kullanıcılara bildirim yapmak ya da bunu gece saatlerinde yapmak mantıklı olur. Ben genellikle bu iş için basit bir wrapper script kullanıyorum.
Nginx ve Apache Sonrası Yenileme Hooks
Sertifika başarıyla yenilendikten sonra web sunucusunun yeni sertifikayı yüklemesi için reload edilmesi gerekiyor. Bunu manuel yapabilirsin ama otomatik yenileme için hook kullanmak çok daha sağlıklı.
# Deploy hook dizinini kontrol et
ls /etc/letsencrypt/renewal-hooks/deploy/
# Nginx için yenileme sonrası reload hook oluştur
cat > /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh << 'EOF'
#!/bin/bash
systemctl reload nginx
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh
# Apache için
cat > /etc/letsencrypt/renewal-hooks/deploy/apache-reload.sh << 'EOF'
#!/bin/bash
systemctl reload apache2
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/apache-reload.sh
Hook dizinleri şu şekilde çalışır:
- /etc/letsencrypt/renewal-hooks/pre/: Yenileme öncesi çalışır
- /etc/letsencrypt/renewal-hooks/deploy/: Başarılı yenileme sonrası çalışır
- /etc/letsencrypt/renewal-hooks/post/: Yenileme girişimi sonrası (başarılı ya da değil) çalışır
Wildcard Sertifika Yenileme
Wildcard sertifikalar (*.example.com) HTTP-01 challenge ile yenilenemez. Bunlar için DNS-01 challenge zorunlu. Bu da DNS sağlayıcının API’si üzerinden otomatik TXT kaydı oluşturabilmek anlamına geliyor.
Cloudflare kullananlar için örnek:
# Cloudflare plugin kur
pip install certbot-dns-cloudflare
# veya
apt install python3-certbot-dns-cloudflare
# Cloudflare credentials dosyası oluştur
cat > /etc/letsencrypt/cloudflare.ini << 'EOF'
dns_cloudflare_api_token = YOUR_API_TOKEN_HERE
EOF
chmod 600 /etc/letsencrypt/cloudflare.ini
# Wildcard sertifika yenile
certbot renew --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini --cert-name example.com
Eğer DNS sağlayıcın için hazır plugin yoksa, manuel DNS challenge yapman gerekir. Bu durumda otomatik yenileme yapılamaz, her yenileme sırasında manuel müdahale gerekir:
certbot certonly --manual --preferred-challenges dns -d "*.example.com" -d "example.com"
Bu komut çalıştırıldığında senden DNS’e bir TXT kaydı eklenmesini isteyecek. Kaydı ekleyip propagasyon tamamlandıktan sonra devam edebilirsin. Wildcard sertifika kullanıyorsan ve DNS sağlayıcın API desteği sunmuyorsa, sağlayıcı değiştirmeyi düşünebilirsin. Üç ayda bir bu işi manuel yapmak ciddi anlamda yorucu.
Docker ve Konteyner Ortamlarında Durum
Docker üzerinde çalışan servislerde sertifika yenileme biraz daha katmanlı bir iş. Sertifika dosyaları host üzerinde tutulup volume olarak konteynera bağlanıyorsa, yenileme host üzerinde yapılır ama konteynerin reload edilmesi gerekir.
# Docker içindeki Nginx'i reload etmek için hook
cat > /etc/letsencrypt/renewal-hooks/deploy/docker-nginx-reload.sh << 'EOF'
#!/bin/bash
CONTAINER_NAME="nginx-proxy"
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
docker exec ${CONTAINER_NAME} nginx -s reload
echo "Nginx container reloaded successfully"
else
echo "Container ${CONTAINER_NAME} not found or not running"
exit 1
fi
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/docker-nginx-reload.sh
Docker Compose kullananlar için:
cat > /etc/letsencrypt/renewal-hooks/deploy/docker-compose-reload.sh << 'EOF'
#!/bin/bash
cd /opt/myapp
docker-compose exec -T nginx nginx -s reload
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/docker-compose-reload.sh
Sertifika Yenilenemiyorsa: Rate Limit Durumu
Let’s Encrypt’in rate limit’leri şunlardır:
- Aynı registered domain için haftada 50 sertifika
- Aynı sertifika için haftada 5 yenileme
- Başarısız doğrulama için saatte 5 deneme
Eğer rate limit’e takıldıysan, staging ortamında test et:
# Staging ile test et (rate limit yok ama güvenilir CA değil)
certbot renew --dry-run --staging
# Staging sertifikası al (test amaçlı)
certbot certonly --staging -d example.com
Rate limit kontrolü için https://crt.sh/?q=example.com adresinden son sertifikaları görebilirsin. Eğer gerçekten rate limit’e takıldıysan ve mevcut sertifikan dolmak üzereyse, eski sertifikayı bir süre daha kullanmaya devam etmekten başka seçeneğin yok. Let’s Encrypt’in rate limit penceresi genellikle 7 gün.
Renewal Config Dosyasını Düzeltmek
Bazen sorun yenileme konfigürasyon dosyasında olur. Her domain için /etc/letsencrypt/renewal/ altında bir .conf dosyası var.
cat /etc/letsencrypt/renewal/example.com.conf
Çıktı şuna benzer:
[renewalparams]
account = abc123...
authenticator = webroot
webroot_path = /var/www/html
server = https://acme-v02.api.letsencrypt.org/directory
[[webroot_map]]
example.com = /var/www/html
www.example.com = /var/www/html
Eğer authenticator yanlışsa veya webroot_path artık geçerli değilse, bu dosyayı düzenleyebilirsin. Ama dikkatli ol, yanlış düzenleme sertifika yenilemeyi tamamen kırabilir. Düzenleme sonrası mutlaka --dry-run ile test et.
# Konfigürasyonu güncelle ve test et
certbot renew --cert-name example.com --dry-run
Monitoring ve Proaktif Yaklaşım
Sertifika süresi dolmadan uyarı almak için birkaç yöntem var.
Basit bir bash scripti ile cron:
cat > /usr/local/bin/check-ssl-expiry.sh << 'EOF'
#!/bin/bash
DOMAINS=("example.com" "api.example.com" "mail.example.com")
WARNING_DAYS=14
ALERT_EMAIL="[email protected]"
for DOMAIN in "${DOMAINS[@]}"; do
EXPIRY=$(echo | openssl s_client -connect ${DOMAIN}:443 -servername ${DOMAIN} 2>/dev/null |
openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -z "$EXPIRY" ]; then
echo "WARNING: Could not check SSL for ${DOMAIN}" | mail -s "SSL Check Failed: ${DOMAIN}" ${ALERT_EMAIL}
continue
fi
EXPIRY_EPOCH=$(date -d "${EXPIRY}" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ ${DAYS_LEFT} -lt ${WARNING_DAYS} ]; then
echo "SSL certificate for ${DOMAIN} expires in ${DAYS_LEFT} days (${EXPIRY})" |
mail -s "SSL Expiry Warning: ${DOMAIN}" ${ALERT_EMAIL}
fi
done
EOF
chmod +x /usr/local/bin/check-ssl-expiry.sh
# Cron'a ekle - her gün sabah 8'de çalışsın
echo "0 8 * * * root /usr/local/bin/check-ssl-expiry.sh" >> /etc/cron.d/ssl-check
Let’s Encrypt kendi de sertifika süresi dolmadan [email protected] adresinden uyarı e-postası gönderiyor, ama buna güvenmek tek başına yeterli değil. Kendi monitoring’ini kur.
Acil Durum: Sertifika Doldu, Site Erişilemiyor
En kötü senaryo: sertifika doldu, kullanıcılar “Your connection is not private” hatası alıyor. Panik yapmadan sırayla ilerle.
İlk adım, mevcut durumu doğrula:
# Sertifika bilgisini kontrol et
certbot certificates
# Nginx/Apache durumunu kontrol et
systemctl status nginx
journalctl -u nginx --since "1 hour ago" | tail -30
Sonra yenileme dene:
# Önce dry-run
certbot renew --dry-run --cert-name example.com
# Sorun yoksa gerçek yenileme
certbot renew --cert-name example.com --force-renewal
--force-renewal flagı normalde 30 günden önce yenileme yapmaz, bunu bypass eder. Sertifika dolmuşsa zaten 30 gün limiti aşılmış demektir ama bazen manuel zorlamak gerekebilir.
Yenileme başarılıysa web sunucusunu reload et:
nginx -t && systemctl reload nginx
# veya
apachectl configtest && systemctl reload apache2
Eğer yenileme bir sebepten çalışmıyorsa ve site tamamen erişilemez durumdaysa, geçici çözüm olarak self-signed sertifika kullanabilirsin. Evet, tarayıcı uyarı verecek ama en azından teknik olarak HTTPS çalışır ve siz arkada sorunu çözmeye devam edebilirsiniz:
# Geçici self-signed sertifika oluştur
openssl req -x509 -nodes -days 7 -newkey rsa:2048
-keyout /tmp/temp-key.pem
-out /tmp/temp-cert.pem
-subj "/CN=example.com"
Bu kesinlikle kalıcı bir çözüm değil, sadece can simidi.
Otomatik Yenilemeyi Sağlam Hale Getirmek
Tüm bu kriz senaryolarını yaşamamak için otomatik yenilemenin düzgün çalıştığından emin olman lazım. Sistemd timer tercih edilen yöntem:
# Timer'ı kontrol et
systemctl status certbot.timer
# Aktif değilse etkinleştir
systemctl enable certbot.timer
systemctl start certbot.timer
# Timer ayarlarını gör
systemctl cat certbot.timer
Eğer sisteminizde timer yoksa veya cron kullanmak istiyorsanız:
# /etc/cron.d/certbot dosyasını oluştur veya düzenle
cat > /etc/cron.d/certbot << 'EOF'
# Her gün gece 2:30 ve öğlen 14:30'da çalışır
# Let's Encrypt iki kez günlük öneriyor
0 */12 * * * root test -x /usr/bin/certbot -a ! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew
EOF
perl -e 'sleep int(rand(43200))' kısmı rastgele bir gecikme ekliyor. Bu Let’s Encrypt sunucularına yük dağıtmak için önerilen bir yaklaşım.
Sonuç
Let’s Encrypt sertifika sorunları gerçekte iki kategoriye giriyor: ya otomatik yenileme kırılmış ve kimse fark etmemiş, ya da ilk kurulum sırasında doğru yapılandırılmamış. Her iki durumda da çözüm aynı mantığı izliyor: önce mevcut durumu anla, sonra manuel yenileme ile krizi atla, ardından otomatik yenilemenin neden çalışmadığını bul ve düzelt.
Kritik birkaç şeyi her zaman aklında tut: --dry-run senin en iyi dostuşur, hook mekanizmasını kullan çünkü sertifika yenilense bile web sunucusu reload edilmezse işe yaramaz, ve wildcard sertifikalar için DNS sağlayıcının API desteğini şimdiden araştır.
Son olarak, monitoring konusunu hafife alma. Let’s Encrypt’in kendi uyarı e-postası bir güvenlik ağı ama kendi SSL kontrol scriptini kurmak çok daha güvenilir. Bir sertifika sorununu kullanıcılar senden önce fark ederse, bu durumun sysadmin kariyerinde bıraktığı iz nahoştur. Bunu yaşamak zorunda değilsin.
