Apache SSL Sertifika Sorunlarını Giderme
SSL sertifika sorunları, production ortamında en kötü zamanlarda karşınıza çıkan türden problemlerdir. Saat gece 2, telefon çalıyor ve müşteri sitesinin tarayıcılarda “Your connection is not private” hatası verdiğini söylüyor. Bu senaryoyu yaşayan her sysadmin bilir: panik yapmadan, sistematik yaklaşmak şart. Bu yazıda Apache üzerindeki SSL sertifika sorunlarını kök nedenlerine inerek nasıl çözeceğinizi, hangi logları nasıl okuyacağınızı ve gelecekte aynı sorunu yaşamamak için ne yapmanız gerektiğini ele alacağım.
SSL Sorunlarını Anlamak: Nereden Başlamalı?
Apache’de SSL sorunları genellikle birkaç ana kategoriye girer. Sertifikanın süresi dolmuştur, sertifika zinciri eksiktir, özel anahtar ile sertifika eşleşmemektedir ya da Apache konfigürasyonunda bir hata vardır. Bunların hepsinin belirtileri birbirine benzeyebilir ama çözüm yolları farklıdır.
İlk iş olarak Apache error log’una bakın. Çoğu zaman sorunun tam açıklaması orada yazıyor olur:
# Gerçek zamanlı log takibi
tail -f /var/log/apache2/error.log
# SSL ile ilgili satırları filtrele
grep -i "ssl|certificate|handshake" /var/log/apache2/error.log | tail -50
# Belirli bir virtual host'un logunu izle
tail -f /var/log/apache2/yourdomain.com-error.log
Apache loglarında göreceğiniz tipik SSL hata mesajları şunlardır:
- AH02032: Handshake başarısız, genellikle cipher suite uyumsuzluğu
- AH01964: Bağlantı yeniden deneniyor, sertifika doğrulama hatası
- SSL_ERROR_RX_RECORD_TOO_LONG: HTTP portu üzerinden SSL bağlantısı denenmiş
- certificate verify failed: Sertifika zinciri sorunu
Sertifika Geçerliliğini Komut Satırından Kontrol Etme
Logları inceledikten sonra sertifikanın kendisine bakmak gerekir. openssl komutu burada en iyi arkadaşınızdır.
# Uzak sunucunun sertifikasını kontrol et
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
# Sertifika detaylarını göster (son satırları kes)
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -text
# Sadece geçerlilik tarihlerine bak
openssl s_client -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates
# Sertifika dosyasını doğrudan incele
openssl x509 -in /etc/ssl/certs/yourdomain.crt -noout -text
openssl x509 -in /etc/ssl/certs/yourdomain.crt -noout -dates
openssl x509 -in /etc/ssl/certs/yourdomain.crt -noout -subject -issuer
openssl s_client çıktısında dikkat etmeniz gereken birkaç kritik alan vardır. Verify return code: 0 (ok) görüyorsanız sertifika zinciri tam demektir. Farklı bir kod görüyorsanız sorun oradadır.
Özel Anahtar ile Sertifika Eşleşmesini Doğrulama
Bu, gözden kaçan ama çok yaygın bir sorundur. Sertifika yenilenmiş ama eski özel anahtar bırakılmıştır ya da tam tersi. Apache bu durumda başlarken hata verir veya hiç başlamaz.
# Sertifika ve özel anahtarın modulus değerlerini karşılaştır
openssl x509 -noout -modulus -in /etc/ssl/certs/yourdomain.crt | md5sum
openssl rsa -noout -modulus -in /etc/ssl/private/yourdomain.key | md5sum
# CSR ile sertifikanın eşleşip eşleşmediğini kontrol et
openssl req -noout -modulus -in yourdomain.csr | md5sum
Bu üç komutun çıktıları birbirinin aynısı olmalıdır. Farklı çıktı alıyorsanız elinizde yanlış dosyalar var demektir. Gerçek dünyada bu durumu çok sık yaşıyoruz: CA’dan gelen yeni sertifika /etc/ssl/certs/ altına atılmış ama /etc/apache2/sites-available/ içindeki virtual host konfigürasyonu hâlâ eski sertifikayı gösteriyor.
Sertifika Zinciri Sorunları
Zincir sorunu (chain issues), özellikle kurumsal CA’lardan alınan sertifikalarda sık karşılaşılan bir durumdur. Tarayıcı sertifikayı doğrulayabilmek için root CA’ya kadar olan tüm ara sertifikaları (intermediate certificates) görmek ister.
# Zincirin tam olup olmadığını kontrol et
openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt /etc/ssl/certs/yourdomain.crt
# Tüm zinciri göster
openssl s_client -connect yourdomain.com:443 -showcerts 2>/dev/null
# Sertifika zincirini ayrı ayrı doğrula
openssl verify -untrusted /etc/ssl/certs/intermediate.crt /etc/ssl/certs/yourdomain.crt
Sertifika zincirini Apache konfigürasyonunda belirtmek için SSLCertificateChainFile direktifini (Apache 2.4.8 öncesi) ya da modern yaklaşımda tüm zinciri tek bir dosyada birleştirmeyi kullanabilirsiniz:
# Sertifika zincirini birleştir (sıra önemli: önce domain sertifikası, sonra intermediate)
cat yourdomain.crt intermediate.crt > yourdomain_fullchain.crt
# Birleşik dosyayı doğrula
openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt yourdomain_fullchain.crt
Apache konfigürasyonunda bu dosyayı şu şekilde kullanırsınız:
<VirtualHost *:443>
ServerName yourdomain.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/yourdomain_fullchain.crt
SSLCertificateKeyFile /etc/ssl/private/yourdomain.key
# Apache 2.4.8+ için artık SSLCertificateChainFile kullanmıyoruz
# Eski sürümler için:
# SSLCertificateChainFile /etc/ssl/certs/intermediate.crt
</VirtualHost>
Apache SSL Konfigürasyonunu Doğrulama
Konfigürasyon hatalarını bulmak için Apache’nin yerleşik test aracını kullanın:
# Syntax kontrolü
apache2ctl configtest
# veya
apachectl -t
# Tüm konfigürasyonu göster (virtual host dahil)
apache2ctl -S
# SSL modülünün yüklenip yüklenmediğini kontrol et
apache2ctl -M | grep ssl
# Çıktıda "ssl_module (shared)" görmeliyiz
# Hangi sertifika dosyalarının kullanıldığını bul
grep -r "SSLCertificate" /etc/apache2/sites-enabled/
Gerçek bir senaryodan bahsedeyim: Bir e-ticaret sitesinde sertifika yenileme sonrası Apache yeniden başlatılmış ama apache2ctl configtest atlanmış. Sertifika dosyası yanlış dizine kopyalanmıştı ve Apache “graceful restart” ile çalışmaya devam etmişti eski sertifikayla. Bunu anlamak için şu komutu kullandık:
# Apache'nin şu an hangi dosyaları açık tuttuğunu gör
lsof -p $(cat /var/run/apache2/apache2.pid) | grep -i ssl
# veya tüm apache prosesleri için
lsof -c apache2 | grep -i ".crt|.pem|.key"
Bu komut, çalışan Apache process’inin gerçekte hangi sertifika dosyasını kullandığını gösterir. Konfigürasyonda yazan ile çalışan arasında fark varsa, Apache yeniden başlatılmamış demektir.
Let’s Encrypt Sertifikalarında Yaygın Sorunlar
Let’s Encrypt kullananlar için birkaç spesifik durum daha var. Certbot ile yönetilen sertifikaların otomatik yenilenmesi bazen sessiz sedasız başarısız olabilir.
# Sertifika yenileme durumunu kontrol et
certbot certificates
# Yenileme simülasyonu yap (gerçekten yenilemeden test et)
certbot renew --dry-run
# Belirli bir domain için yenile
certbot renew --cert-name yourdomain.com
# Systemd timer durumunu kontrol et
systemctl status certbot.timer
systemctl list-timers | grep certbot
# Certbot loglarına bak
cat /var/log/letsencrypt/letsencrypt.log | tail -100
Let’s Encrypt’te sık karşılaşılan bir sorun: webroot ya da standalone doğrulama sırasında port 80 erişiminin engellenmesi. Bunu test etmek için:
# 80 numaralı portun dışarıdan erişilebilir olduğunu kontrol et
curl -I http://yourdomain.com/.well-known/acme-challenge/test
# Firewall durumuna bak
ufw status
# veya
firewall-cmd --list-all
# Apache'nin 80 portunu dinleyip dinlemediğini kontrol et
ss -tlnp | grep :80
Certbot’un Apache plugin’i kullanılıyorsa, sertifika yenilendikten sonra Apache’nin otomatik reload edilmesi gerekir. Bu bazen çalışmaz:
# Manuel reload dene
systemctl reload apache2
# Certbot deploy hook'u kontrol et
ls -la /etc/letsencrypt/renewal-hooks/deploy/
cat /etc/letsencrypt/renewal-hooks/deploy/apache-reload.sh
SSL Handshake Hatalarını Derinlemesine İnceleme
SSL handshake failed hatası birçok farklı nedenden kaynaklanabilir. Bu hatayı debug etmek için önce hangi adımda başarısız olduğunu anlamak gerekir.
# Detaylı SSL debug bilgisi al
openssl s_client -connect yourdomain.com:443 -state -debug 2>&1 | head -50
# Belirli bir TLS versiyonunu zorla test et
openssl s_client -connect yourdomain.com:443 -tls1_2
openssl s_client -connect yourdomain.com:443 -tls1_3
# Desteklenen cipher'ları listele
openssl ciphers -v 'HIGH:!aNULL:!MD5'
# nmap ile SSL taraması yap
nmap --script ssl-enum-ciphers -p 443 yourdomain.com
Apache konfigürasyonunda cipher suite ve TLS versiyon kısıtlamaları handshake hatalarına neden olabilir. Modern ve güvenli bir konfigürasyon şöyle görünür:
# /etc/apache2/conf-available/ssl-params.conf
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLUseStapling on
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
OCSP Stapling Sorunları
OCSP stapling etkinleştirilmiş ama düzgün çalışmıyorsa tarayıcı sertifika durumunu doğrulamak için harici sunuculara gider ve bu gecikmelere ya da hatalara neden olur.
# OCSP stapling durumunu test et
openssl s_client -connect yourdomain.com:443 -status 2>/dev/null | grep -A 10 "OCSP response"
# OCSP yanıtını manuel olarak sorgula
openssl x509 -in /etc/ssl/certs/yourdomain.crt -noout -ocsp_uri
# Çıktıdaki OCSP URL'ini kullan:
openssl ocsp -issuer /etc/ssl/certs/intermediate.crt
-cert /etc/ssl/certs/yourdomain.crt
-url http://ocsp.yourca.com
-resp_text
OCSP stapling çalışmıyorsa Apache error log’da şunu görürsünüz: AH01941: stapling_renew_response: responder error. Bu durumda Apache’nin OCSP sunucusuna erişip erişemediğini kontrol edin:
# Apache kullanıcısı olarak OCSP sunucusuna bağlanmayı test et
sudo -u www-data curl -v http://ocsp.yourca.com
SNI (Server Name Indication) Sorunları
Tek IP üzerinde birden fazla SSL domain barındırıyorsanız SNI devreye girer. SNI sorunları özellikle eski istemcilerle ya da yanlış konfigürasyonlarda ortaya çıkar.
# SNI ile bağlantı test et
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
# SNI olmadan bağlantı test et (default virtual host devreye girer)
openssl s_client -connect yourdomain.com:443
# Hangi sertifikanın döndüğünü gör
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -subject
# Tüm virtual host'ların SSL konfigürasyonunu listele
apache2ctl -D DUMP_VHOSTS 2>/dev/null | grep -A 5 "443"
Eğer SNI çalışmıyorsa ve yanlış sertifika dönüyorsa, büyük ihtimalle _default_:443 veya *:443 olan varsayılan virtual host devreye giriyor demektir. Her virtual host’un ServerName direktifinin doğru ayarlandığından emin olun.
İzleme ve Önleyici Tedbirler
Sorun olduktan sonra koşturmak yerine önleyici bir sistem kurmak çok daha verimlidir. İşte basit ama etkili birkaç yöntem:
# Sertifikanın kaç gün sonra dolacağını hesapla
EXPIRY=$(openssl x509 -enddate -noout -in /etc/ssl/certs/yourdomain.crt | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "Sertifika $DAYS_LEFT gun sonra doluyor"
# 30 günden az kaldıysa uyarı ver
if [ $DAYS_LEFT -lt 30 ]; then
echo "UYARI: Sertifika 30 gun icinde doluyor!" | mail -s "SSL Uyarisi" [email protected]
fi
Bu scripti cron’a ekleyerek günlük kontrol yapabilirsiniz:
# Crontab'a ekle
echo "0 9 * * * /usr/local/bin/check_ssl_expiry.sh" | crontab -
# Nagios/Zabbix kullanıyorsanız check_ssl_cert plugin'i tercih edin
/usr/lib/nagios/plugins/check_http -H yourdomain.com --ssl -C 30,14
Birden fazla domaini izlemek için daha kapsamlı bir script:
#!/bin/bash
# /usr/local/bin/check_all_ssl.sh
DOMAINS=("yourdomain.com" "api.yourdomain.com" "admin.yourdomain.com")
WARN_DAYS=30
CRITICAL_DAYS=14
for DOMAIN in "${DOMAINS[@]}"; do
EXPIRY=$(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" ]; then
echo "KRITIK: $DOMAIN - Sertifikaya erisilemedii"
continue
fi
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt $CRITICAL_DAYS ]; then
echo "KRITIK: $DOMAIN - $DAYS_LEFT gun kaldi"
elif [ $DAYS_LEFT -lt $WARN_DAYS ]; then
echo "UYARI: $DOMAIN - $DAYS_LEFT gun kaldi"
else
echo "OK: $DOMAIN - $DAYS_LEFT gun kaldi"
fi
done
Hızlı Sorun Giderme Kontrol Listesi
Gece 2’de alarm geldiğinde takip edebileceğiniz sıralı adımlar:
- Apache error log’a bakın:
tail -100 /var/log/apache2/error.log | grep -i ssl - Sertifika tarihini kontrol edin:
openssl x509 -in /path/to/cert.crt -noout -dates - Anahtar-sertifika eşleşmesini doğrulayın: modulus hash’lerini karşılaştırın
- Konfigürasyon syntax’ını test edin:
apache2ctl configtest - Apache’nin çalışan versiyonunu kontrol edin:
apache2ctl -V | grep version - SSL modülünün yüklendiğinden emin olun:
apache2ctl -M | grep ssl - Port 443’ün dinlendiğini doğrulayın:
ss -tlnp | grep :443 - Firewall kurallarını inceleyin:
ufw status verbose - openssl ile dışarıdan test edin:
openssl s_client -connect domain.com:443
Sonuç
Apache SSL sorunları sistematik yaklaşıldığında çoğunlukla hızlıca çözülür. Panik yapmak yerine loglardan başlayıp openssl araçlarıyla ilerleyerek sorunun tam olarak nerede olduğunu tespit etmek kritiktir. Sertifika-anahtar uyumsuzluğu, eksik zincir ve süresi dolmuş sertifika, karşılaşılan sorunların büyük çoğunluğunu oluşturur.
Uzun vadede ise izleme ve otomasyon şarttır. Let’s Encrypt kullanıyorsanız otomatik yenilemenin gerçekten çalışıp çalışmadığını düzenli kontrol edin. Ticari sertifika kullanıyorsanız takvim hatırlatıcıları ve monitoring scriptleri hayat kurtarıcıdır. En iyi SSL sorunu, hiç yaşanmayan SSL sorunudur.
Bir dahaki sefer production’da SSL alarmı geldiğinde bu yazıyı referans olarak kullanabilirsiniz. Yorum kısmında karşılaştığınız farklı senaryoları paylaşırsanız, bu listeyi zenginleştirebiliriz.
