HAProxy ile SSL Passthrough ve SSL Termination Yapılandırması
Yük dengeleme dünyasında HAProxy, yıllardır sistem yöneticilerinin vazgeçilmezi olmaya devam ediyor. Özellikle SSL/TLS trafiğini yönetme konusunda iki temel yaklaşım var: SSL Passthrough ve SSL Termination. Her ikisi de farklı senaryolarda hayat kurtarıcı ama hangisini ne zaman kullanacağını bilmek, hem güvenlik hem de performans açısından kritik öneme sahip. Bu yazıda her iki yaklaşımı da production ortamında nasıl yapılandıracağını, avantaj ve dezavantajlarıyla birlikte ele alacağız.
SSL Termination ve SSL Passthrough Nedir?
Önce kavramları netleştirelim çünkü ikisi arasındaki farkı anlamadan doğru yapılandırmayı seçemezsin.
SSL Termination: HAProxy, istemciden gelen şifreli HTTPS trafiğini kendi üzerinde çözer (decrypt eder), backend sunucularla HTTP veya yeniden şifrelenmiş HTTPS üzerinden iletişim kurar. Sertifika yönetimi tamamen HAProxy üzerinde yapılır.
SSL Passthrough: HAProxy, şifreli TLS trafiğine hiç dokunmaz. Paketi olduğu gibi backend sunucuya iletir. Şifre çözme işlemi tamamen backend’de gerçekleşir. HAProxy burada kör bir ağ yönlendirici gibi davranır.
Gerçek dünya senaryosunda ne zaman hangisini kullanırsın?
- SSL Termination: İçerik bazlı yük dengeleme yapacaksan, HTTP header’larını okuman gerekiyorsa, merkezi sertifika yönetimi istiyorsan
- SSL Passthrough: End-to-end şifreleme zorunluysa (uyumluluk gereksinimleri), backend uygulamanın client sertifikası doğrulaması yapması gerekiyorsa, mutual TLS (mTLS) senaryolarında
HAProxy Kurulumu
Önce HAProxy’yi sisteme kuralım. Ubuntu/Debian için:
sudo apt update
sudo apt install -y haproxy
# Versiyon kontrolü
haproxy -v
# Servisi başlat ve otomatik başlatmayı etkinleştir
sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status haproxy
CentOS/RHEL/Rocky Linux için:
sudo dnf install -y haproxy
sudo systemctl enable --now haproxy
# Firewall kurallarını ekle
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
HAProxy versiyon 2.x kullanmanı öneririm. Eski 1.8.x sürümleri bazı TLS özelliklerini desteklemiyor.
SSL Termination Yapılandırması
SSL Termination senaryosunda önce sertifikamızı hazırlamamız gerekiyor. HAProxy, sertifika ve private key’i tek bir PEM dosyasında bekler.
# Sertifika ve private key'i birleştir
cat /etc/ssl/certs/example.com.crt /etc/ssl/private/example.com.key > /etc/haproxy/certs/example.com.pem
# Dosya izinlerini ayarla
sudo chmod 600 /etc/haproxy/certs/example.com.pem
sudo chown haproxy:haproxy /etc/haproxy/certs/example.com.pem
# Dizin izinleri
sudo chmod 700 /etc/haproxy/certs/
Let’s Encrypt kullanıyorsan Certbot ile sertifika almak ve HAProxy için hazırlamak şöyle yapılır:
# Certbot kurulumu
sudo apt install -y certbot
# Standalone modda sertifika al (HAProxy'yi geçici durdur)
sudo systemctl stop haproxy
sudo certbot certonly --standalone -d example.com -d www.example.com
sudo systemctl start haproxy
# HAProxy için PEM dosyası oluştur
sudo cat /etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem
> /etc/haproxy/certs/example.com.pem
sudo chmod 600 /etc/haproxy/certs/example.com.pem
# Otomatik yenileme için hook scripti
sudo nano /etc/letsencrypt/renewal-hooks/deploy/haproxy-deploy.sh
Şimdi asıl HAProxy SSL Termination yapılandırmasına geçelim. /etc/haproxy/haproxy.cfg dosyasını düzenle:
# /etc/haproxy/haproxy.cfg
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
# SSL/TLS ayarları
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
errorfile 400 /etc/haproxy/errors/400.http
errorfile 503 /etc/haproxy/errors/503.http
# HTTP'yi HTTPS'e yönlendir
frontend http_redirect
bind *:80
mode http
option forwardfor
redirect scheme https code 301 if !{ ssl_fc }
# HTTPS Frontend - SSL Termination
frontend https_frontend
bind *:443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
mode http
option forwardfor
option http-server-close
# HSTS header ekle
http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# X-Forwarded-Proto header
http-request set-header X-Forwarded-Proto https
# ACL ile yönlendirme
acl is_api hdr(host) -i api.example.com
acl is_app hdr(host) -i app.example.com
use_backend api_servers if is_api
use_backend app_servers if is_app
default_backend app_servers
# Backend sunucular
backend api_servers
mode http
balance roundrobin
option httpchk GET /health
http-check expect status 200
server api1 192.168.1.10:8080 check inter 2s rise 2 fall 3
server api2 192.168.1.11:8080 check inter 2s rise 2 fall 3
server api3 192.168.1.12:8080 check inter 2s rise 2 fall 3 backup
backend app_servers
mode http
balance leastconn
option httpchk GET /ping
http-check expect status 200
cookie SERVERID insert indirect nocache
server app1 192.168.1.20:3000 check cookie app1
server app2 192.168.1.21:3000 check cookie app2
Bu yapılandırmada birkaç önemli detaya dikkat etmek gerekiyor:
- ssl-min-ver TLSv1.2: TLS 1.0 ve 1.1’i devre dışı bırakır, bu günümüzde zorunlu
- no-tls-tickets: TLS session ticket’larını kapatır, perfect forward secrecy için önemli
- alpn h2,http/1.1: HTTP/2 desteğini aktif eder
- option forwardfor: İstemcinin gerçek IP’sini X-Forwarded-For header’ına ekler
SSL Passthrough Yapılandırması
SSL Passthrough için HAProxy’nin TCP modunda çalışması gerekiyor. Bu durumda HTTP header’larına erişimin olmadığını unutma.
# /etc/haproxy/haproxy.cfg - SSL Passthrough senaryosu
global
log /dev/log local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
user haproxy
group haproxy
daemon
defaults
log global
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
# TCP Frontend - SSL Passthrough
frontend tls_passthrough
bind *:443
mode tcp
option tcplog
# SNI tabanlı yönlendirme (Layer 4)
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
# SNI'ya göre backend seçimi
acl is_api req_ssl_sni -i api.example.com
acl is_db req_ssl_sni -i db.example.com
acl is_app req_ssl_sni -i app.example.com
use_backend api_tls_servers if is_api
use_backend db_tls_servers if is_db
default_backend app_tls_servers
backend api_tls_servers
mode tcp
balance roundrobin
option ssl-hello-chk
server api1 192.168.1.10:443 check
server api2 192.168.1.11:443 check
backend db_tls_servers
mode tcp
balance source
server db1 192.168.1.30:5432 check
server db2 192.168.1.31:5432 check
backend app_tls_servers
mode tcp
balance leastconn
server app1 192.168.1.20:443 check
server app2 192.168.1.21:443 check
SSL Passthrough yapılandırmasında kritik noktalar:
- tcp-request inspect-delay 5s: HAProxy’nin SNI bilgisini okumak için paketi beklemesini sağlar
- req_ssl_hello_type 1: Client Hello mesajını bekler, bu olmadan SNI okunamaz
- req_ssl_sni: SNI hostname’ine göre ACL tanımlaması
- option ssl-hello-chk: Backend SSL sağlık kontrolü yapar
Hibrit Yaklaşım: Hem Termination Hem Passthrough
Gerçek production ortamlarında çoğu zaman her iki yöntemi aynı anda kullanmak gerekir. Mesela bazı servisler için SSL termination yaparken, kritik veritabanı veya ödeme sistemi gibi servisler için passthrough kullanmak isteyebilirsin.
# /etc/haproxy/haproxy.cfg - Hibrit yapılandırma
global
log /dev/log local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
user haproxy
group haproxy
daemon
maxconn 50000
# Global SSL ayarları
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
tune.ssl.default-dh-param 2048
defaults
log global
option dontlognull
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
# Port 443'te TCP modunda başla, SNI'ya göre yönlendir
frontend mixed_443
bind *:443
mode tcp
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
# Passthrough gerektiren servisler
acl needs_passthrough req_ssl_sni -i payment.example.com
acl needs_passthrough req_ssl_sni -i secure-db.example.com
# Termination yapılacaklar HAProxy'nin localhost portuna yönlendir
use_backend direct_passthrough if needs_passthrough
default_backend local_termination
# Termination için dahili backend (localhost'a yönlendir)
backend local_termination
mode tcp
server localhost 127.0.0.1:8443
# Doğrudan passthrough backend'leri
backend direct_passthrough
mode tcp
balance roundrobin
server payment1 192.168.1.50:443 check
server payment2 192.168.1.51:443 check
# SSL Termination frontend (sadece localhost'tan gelir)
frontend ssl_termination
bind 127.0.0.1:8443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
mode http
option forwardfor
option http-server-close
http-request set-header X-Forwarded-Proto https
http-response set-header Strict-Transport-Security "max-age=63072000"
acl is_api hdr(host) -i api.example.com
acl is_app hdr(host) -i app.example.com
acl is_admin hdr(host) -i admin.example.com
use_backend api_http_servers if is_api
use_backend admin_servers if is_admin
default_backend app_http_servers
backend api_http_servers
mode http
balance roundrobin
option httpchk GET /health
server api1 192.168.1.10:8080 check
server api2 192.168.1.11:8080 check
backend app_http_servers
mode http
balance leastconn
server app1 192.168.1.20:3000 check
server app2 192.168.1.21:3000 check
backend admin_servers
mode http
balance source
server admin1 192.168.1.40:8090 check
Yapılandırma Doğrulama ve Test
Yapılandırmayı her değişiklikten önce mutlaka test et:
# Syntax kontrolü
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
# Başarılı çıktı:
# Configuration file is valid
# HAProxy'yi yeniden yükle (bağlantıları kesmeden)
sudo systemctl reload haproxy
# Veya graceful restart
sudo haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -sf $(cat /run/haproxy.pid)
# SSL yapılandırmasını test et
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>&1 | grep -E "subject|issuer|Protocol|Cipher"
# TLS versiyonunu kontrol et
openssl s_client -connect example.com:443 -tls1 2>&1 | grep "alert"
openssl s_client -connect example.com:443 -tls1_1 2>&1 | grep "alert"
openssl s_client -connect example.com:443 -tls1_2 2>&1 | grep "Cipher"
# SNI passthrough testi
openssl s_client -connect haproxy-ip:443 -servername payment.example.com </dev/null
# HAProxy istatistikleri
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | cut -d',' -f1,2,18,19 | column -s',' -t
Sertifika Yenileme Otomasyonu
Let’s Encrypt sertifikaları 90 günde bir yenilenir. HAProxy için otomatik yenileme scriptini şöyle yazabilirsin:
#!/bin/bash
# /etc/letsencrypt/renewal-hooks/deploy/haproxy-cert-deploy.sh
DOMAIN="example.com"
HAPROXY_CERT_DIR="/etc/haproxy/certs"
LE_CERT_DIR="/etc/letsencrypt/live/${DOMAIN}"
# PEM dosyasını güncelle
cat "${LE_CERT_DIR}/fullchain.pem"
"${LE_CERT_DIR}/privkey.pem"
> "${HAPROXY_CERT_DIR}/${DOMAIN}.pem"
chmod 600 "${HAPROXY_CERT_DIR}/${DOMAIN}.pem"
chown haproxy:haproxy "${HAPROXY_CERT_DIR}/${DOMAIN}.pem"
# Yapılandırmayı doğrula
if haproxy -c -f /etc/haproxy/haproxy.cfg; then
# Graceful reload
systemctl reload haproxy
echo "$(date): HAProxy sertifika yenilendi ve reload edildi" >> /var/log/haproxy-cert-renewal.log
else
echo "$(date): HAProxy yapılandırma hatası! Sertifika güncellenmedi." >> /var/log/haproxy-cert-renewal.log
exit 1
fi
chmod +x /etc/letsencrypt/renewal-hooks/deploy/haproxy-cert-deploy.sh
# Crontab ile otomatik yenileme (30 günde bir kontrol et)
echo "0 3 * * 1 root certbot renew --quiet" | sudo tee /etc/cron.d/certbot-renew
Yaygın Hatalar ve Çözümleri
Production’da en sık karşılaşılan sorunlar şunlar:
SSL Passthrough’da SNI okunmuyor: tcp-request inspect-delay değerini artır, bazı istemciler ClientHello’yu geç gönderiyor olabilir.
Backend’e güvenli bağlantı kurulamıyor: SSL Termination’dan backend’e de şifreli bağlantı kurmak istiyorsan backend tanımına ssl verify none veya ssl verify required ca-file /etc/ssl/certs/ca-certificates.crt ekle.
HAProxy stats sayfası: Monitoring için açabilirsin, ama mutlaka parola koy:
# haproxy.cfg içine ekle
listen stats
bind *:8404
mode http
stats enable
stats uri /haproxy-stats
stats realm HAProxy Statistics
stats auth admin:guclu_sifre_buraya
stats refresh 30s
stats show-legends
stats show-node
TLS sertifika zinciri hatası: PEM dosyası oluştururken sıralama önemli. Önce sertifika, sonra ara sertifikalar (intermediate), en son private key gelmelidir.
# Doğru sıralama
cat domain.crt intermediate.crt root.crt privkey.key > /etc/haproxy/certs/domain.pem
# Sertifika zincirini doğrula
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt
-untrusted /etc/haproxy/certs/intermediate.crt
/etc/haproxy/certs/domain.crt
Sonuç
HAProxy ile SSL yönetimi, bir kere doğru kurulduktan sonra son derece güvenilir çalışıyor. SSL Termination, merkezi sertifika yönetimi ve içerik tabanlı yönlendirme için ideal seçim. SSL Passthrough ise end-to-end şifrelemenin zorunlu olduğu, backend’in kendi sertifikasını yönetmesi gereken durumlarda kullanılmalı.
Hibrit yaklaşım, büyük ve karmaşık ortamlar için genellikle en pragmatik çözüm. Ödeme sistemleri ve hassas veri işleyen servisler için passthrough, genel web uygulamaları için termination kullanmak hem güvenlik hem de yönetilebilirlik açısından en dengeli yaklaşım.
Son olarak: SSL yapılandırmasını düzenli olarak test et. SSL Labs’ın testini üç ayda bir çalıştırmayı alışkanlık haline getir, cipher suite’leri ve TLS versiyonlarını güncel tut. Güvenlik bu alanda duran bir şey değil, sürekli güncellenmesi gereken bir süreç.
