Apache ile Reverse Proxy Arkasında Gerçek IP Adresini Loglara Doğru Kaydetme
Bir web uygulaması production ortamında çalışırken karşılaşabileceğin en sinir bozucu durumlardan biri, Apache loglarına baktığında tüm isteklerin aynı IP adresinden geliyormuş gibi görünmesidir. Load balancer veya reverse proxy arkasında çalışan bir Apache kurulumunda bu durum neredeyse kaçınılmazdır. Logların hepsinde 10.0.0.1 veya 192.168.1.100 gibi internal bir IP görüyorsun, ama gerçek kullanıcı nerede? Güvenlik olayı araştırıyorsun, DDoS kaynağını bulmaya çalışıyorsun ya da sadece trafik analizine bakıyorsun; ne yaparsan yap, gerçek IP olmadan işin çok zorlaşıyor.
Bu yazıda hem sorunu köküyle anlayacak hem de Apache’nin farklı konfigürasyonlarında gerçek IP adresini loglarına doğru kaydetmek için adım adım çözüm yollarını göreceğiz.
Sorunun Anatomisi
Reverse proxy mimarisini düşün. Kullanıcı tarayıcısından istek gönderiyor, bu istek önce Nginx, HAProxy, AWS ALB veya benzeri bir load balancer’a geliyor. Bu proxy katmanı isteği Apache’ye iletirken kaynak IP adresi artık kullanıcının değil, proxy’nin IP’si oluyor. Apache logladığında da doğal olarak %h veya %a direktifleri proxy’nin adresini yazıyor.
HTTP protokolü bu problemi çözmek için X-Forwarded-For header’ını tanımladı. Proxy katmanı, orijinal client IP’sini bu header içinde Apache’ye iletir. Header değeri şöyle görünür:
X-Forwarded-For: 203.0.113.45, 10.0.0.1, 192.168.1.100
Buradaki ilk IP gerçek kullanıcı adresidir. Sonraki değerler ise zincirdeki ara proxy’leri temsil eder. Apache’nin mod_remoteip modülü tam olarak bu header’ı okuyup REMOTE_ADDR değişkenini gerçek IP ile güncellemek için tasarlanmıştır.
mod_remoteip Modülü
Apache 2.4 ile birlikte gelen mod_remoteip, mod_rpaf ve benzeri üçüncü parti modüllerin yerini almıştır. Eğer Apache 2.4 kullanıyorsan (ki 2024 itibarıyla kesinlikle kullanıyor olmalısın), bu modül sana ihtiyacın olan her şeyi sunuyor.
Modülün Yüklü ve Aktif Olup Olmadığını Kontrol Et
# Yüklü modülleri listele
apache2ctl -M | grep remoteip
# veya apachectl ile
apachectl -M 2>/dev/null | grep remoteip
# Modül aktifse şu çıktıyı göreceksin:
# remoteip_module (shared)
Eğer modül listede yoksa etkinleştirmen gerekiyor:
# Debian/Ubuntu sistemlerde
sudo a2enmod remoteip
sudo systemctl restart apache2
# RHEL/CentOS/AlmaLinux sistemlerde
# /etc/httpd/conf.modules.d/ dizinine bak
# LoadModule remoteip_module modules/mod_remoteip.so
# satırının uncomment edildiğinden emin ol
sudo systemctl restart httpd
Temel Konfigürasyon
En sade haliyle mod_remoteip konfigürasyonu şöyle görünür:
# /etc/apache2/conf-available/remoteip.conf (Debian/Ubuntu)
# veya /etc/httpd/conf.d/remoteip.conf (RHEL tabanlı)
LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 10.0.0.0/8
RemoteIPTrustedProxy 172.16.0.0/12
RemoteIPTrustedProxy 192.168.0.0/16
RemoteIPHeader: Gerçek IP’yi hangi HTTP header’ından okuyacağını belirtir. Standart X-Forwarded-For yerine bazı sistemler X-Real-IP kullanır, ikisi de desteklenir.
RemoteIPTrustedProxy: Bu IP’lerden gelen X-Forwarded-For bilgisine güvenilir. Sadece kendi proxy’lerini buraya eklemelisin. Rastgele bir IP bu header’ı manipüle edip saldırı trafiğini maskelemeye çalışabilir.
Debian/Ubuntu’da bu conf dosyasını aktif etmek için:
sudo a2enconf remoteip
sudo systemctl reload apache2
Log Formatını Güncelleme
Modülü aktif etmek tek başına yeterli değil. Log formatının %h yerine %a kullanması gerekiyor ya da mevcut formatı gözden geçirmen lazım.
Apache’nin varsayılan combined log formatı:
LogFormat "%h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
Buradaki %h direktifi REMOTE_ADDR değerini loglar. mod_remoteip REMOTE_ADDR‘ı güncellediği için %h teorik olarak çalışmalıdır. Ancak bazı Apache versiyonlarında ve konfigürasyonlarda %a direktifini kullanmak daha güvenilir sonuç verir.
Gerçek IP’yi hem loglamak hem de hata ayıklama için ekstra bilgi tutmak istersen özel bir format tanımlayabilirsin:
# Hem orijinal IP hem proxy IP'sini logla
LogFormat "%a %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i" "%{X-Forwarded-For}i"" combined_proxy
# Virtual host içinde kullan
<VirtualHost *:80>
ServerName example.com
CustomLog /var/log/apache2/example.com-access.log combined_proxy
</VirtualHost>
Bu format sayesinde log satırlarında hem Apache’nin tespit ettiği gerçek IP’yi hem de ham X-Forwarded-For header değerini görebilirsin. Bir şeyler yanlış gittiğinde karşılaştırma yapman çok kolaylaşır.
AWS ALB ve CloudFront Senaryosu
AWS ortamında çalışıyorsan, Application Load Balancer’ın arkasındaki Apache’de durum biraz farklı. ALB birden fazla header ekler ve IP aralıkları sürekli değişebilir.
# AWS ALB için konfigürasyon
RemoteIPHeader X-Forwarded-For
# AWS'nin public IP aralıklarını tek tek yazmak yerine
# iç network aralığını trusted olarak işaretle
RemoteIPTrustedProxy 10.0.0.0/8
RemoteIPTrustedProxy 172.16.0.0/12
RemoteIPTrustedProxy 192.168.0.0/16
# ALB'nin kullandığı link-local aralık
RemoteIPTrustedProxy 169.254.0.0/16
CloudFront kullanıyorsan işler biraz daha karmaşık. CloudFront hem X-Forwarded-For hem de CloudFront-Viewer-Address header’ı ekler. Daha güvenilir olan CloudFront’a özgü header’ı kullanabilirsin:
# CloudFront için gerçek IP header'ı
RemoteIPHeader CloudFront-Viewer-Address
# Ya da X-Forwarded-For ile devam edip CloudFront IP'lerini trusted ekle
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxyList /etc/apache2/cloudfront-ips.txt
cloudfront-ips.txt dosyasını AWS’nin sunduğu IP listesiyle düzenli güncellemen gerekir. Bunun için basit bir cron job yazabilirsin:
#!/bin/bash
# /etc/cron.weekly/update-cloudfront-ips
CF_IPS_URL="https://ip-ranges.amazonaws.com/ip-ranges.json"
OUTPUT_FILE="/etc/apache2/cloudfront-ips.txt"
TEMP_FILE=$(mktemp)
curl -s "$CF_IPS_URL" |
python3 -c "
import json, sys
data = json.load(sys.stdin)
for prefix in data['prefixes']:
if prefix.get('service') == 'CLOUDFRONT':
print(prefix['ip_prefix'])
" > "$TEMP_FILE"
if [ -s "$TEMP_FILE" ]; then
mv "$TEMP_FILE" "$OUTPUT_FILE"
systemctl reload apache2
echo "CloudFront IP listesi güncellendi: $(date)"
else
rm "$TEMP_FILE"
echo "HATA: CloudFront IP listesi alınamadı"
exit 1
fi
Nginx + Apache Kombinasyonu
Birçok production ortamında Nginx front-end proxy, Apache ise uygulama sunucusu olarak çalışır. Bu yaygın senaryoda Nginx tarafında da doğru konfigürasyon şart.
Nginx konfigürasyonu:
# /etc/nginx/nginx.conf veya site konfigürasyonu
upstream apache_backend {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://apache_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Apache tarafında da buna uygun konfigürasyon:
# Apache 8080 portunda dinliyor
Listen 8080
<VirtualHost *:8080>
ServerName example.com
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 127.0.0.1
LogFormat "%a %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" real_ip
CustomLog /var/log/apache2/example.com-access.log real_ip
DocumentRoot /var/www/example.com
</VirtualHost>
HAProxy ile Kullanım
HAProxy genellikle X-Forwarded-For header’ını otomatik ekler, ama PROXY protocol da kullanabilir. PROXY protokolü daha güvenilir ve daha performanslıdır çünkü header manipülasyonuna karşı daha dayanıklıdır.
HAProxy konfigürasyonu:
# /etc/haproxy/haproxy.cfg
frontend web_frontend
bind *:80
mode http
option forwardfor
default_backend apache_servers
backend apache_servers
mode http
server apache1 10.0.1.10:80 check
Apache tarafında option forwardfor ile eklenen header’ı okumak için standart mod_remoteip yeterli. Ancak PROXY protokolü kullanıyorsan RemoteIPProxyProtocol direktifine ihtiyacın var:
<VirtualHost *:80>
RemoteIPProxyProtocol On
RemoteIPProxyProtocolExceptions 127.0.0.1
</VirtualHost>
Konfigürasyonu Test Etme ve Doğrulama
Her değişikliğin ardından konfigürasyonun doğru çalışıp çalışmadığını test etmelisin. İşte bunu yapmanın birkaç yolu:
# Apache konfigürasyonunu syntax kontrolü
sudo apache2ctl configtest
# veya
sudo apachectl -t
# Gerçek IP'yi simüle ederek test isteği gönder
curl -H "X-Forwarded-For: 203.0.113.45" http://localhost/
# Logları anlık takip et
tail -f /var/log/apache2/access.log
# Birden fazla proxy katmanı simülasyonu
curl -H "X-Forwarded-For: 203.0.113.45, 10.0.0.1" http://localhost/
PHP veya benzeri bir uygulama çalışıyorsa, $_SERVER['REMOTE_ADDR'] değerinin de güncellendiğini kontrol eden küçük bir test scripti yazabilirsin:
# Test için geçici PHP dosyası oluştur
cat > /var/www/html/iptest.php << 'EOF'
<?php
echo "REMOTE_ADDR: " . $_SERVER['REMOTE_ADDR'] . "n";
echo "HTTP_X_FORWARDED_FOR: " . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? 'yok') . "n";
echo "HTTP_X_REAL_IP: " . ($_SERVER['HTTP_X_REAL_IP'] ?? 'yok') . "n";
EOF
# Test et
curl -H "X-Forwarded-For: 203.0.113.45" http://localhost/iptest.php
# Test sonrası sil
rm /var/www/html/iptest.php
Güvenlik Dikkat Noktaları
mod_remoteip konfigürasyonunda en kritik nokta güvenlik. Yanlış yapılandırılmış bir kurulum ciddi güvenlik açıklarına yol açabilir.
Trusted proxy listesini dar tut: Sadece gerçekten kontrolündeki proxy IP’lerini güvenilir olarak ekle. Geniş bir aralık verirsen, kötü niyetli biri X-Forwarded-For header’ını sahte IP ile doldurabilir ve bu IP Apache’nin loglarına gerçekmiş gibi geçer. Rate limiting veya IP bazlı engelleme mekanizmaların bypass edilmiş olur.
İç ağ aralıklarını doğru belirle: RFC 1918 private aralıkları (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) çoğu kurulumda güvenli şekilde trusted eklenebilir. Ancak bunları internete açık bir proxy üzerinden geçen trafik için trusted yaparsan sorun çıkabilir.
Logları düzenli izle: Konfigürasyonu tamamladıktan sonra logları periyodik kontrol et. Beklenmedik IP aralıklarından gelen X-Forwarded-For değerleri dikkat çekiciyse birisi başıboş proxy’ler üzerinden manipülasyon deniyor olabilir.
# Logdaki X-Forwarded-For değerlerini analiz et
# Anormal header zincirleri için
grep -E "X-Forwarded-For.*,.*,.*,.*," /var/log/apache2/access.log | head -20
# Trusted olmayan kaynaklardan gelen header var mı diye kontrol
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
Mevcut Logları Yeniden Analiz Etme
Bazen konfigürasyonu düzeltmeden önce toplanmış eski logları da gerçek IP bilgisiyle analiz etmen gerekebilir. Proxy arkasında çalışan sistemlerde genellikle X-Forwarded-For bilgisi log satırının sonuna eklenmiş olur. Bu durumda awk veya sed ile geçici çözüm üretebilirsin:
# Eğer log formatında X-Forwarded-For son kolondaysa
# gerçek IP ile yeni bir log dosyası oluştur
awk '
{
# X-Forwarded-For son alan olarak kaydedilmişse
xff = $NF
gsub(/"/, "", xff)
# Virgülle ayrılmış listeden ilk IP'yi al
split(xff, ips, ", ")
real_ip = ips[1]
if (real_ip != "-" && real_ip != "") {
$1 = real_ip
}
print
}' /var/log/apache2/old-access.log > /var/log/apache2/old-access-realip.log
# Analiz için GoAccess veya benzeri araçlarla kullanabilirsin
goaccess /var/log/apache2/old-access-realip.log --log-format=COMBINED
Sorun Giderme
Konfigürasyonu yaptın ama hala proxy IP’si görünüyor. Sık karşılaşılan sorunlar ve çözümleri:
Modül yüklenmemiş: apachectl -M | grep remoteip çıktısı boşsa modül aktif değildir. Yukarıdaki etkinleştirme adımlarını tekrar uygula.
Yanlış header adı: Proxy’nin hangi header’ı eklediğini curl -v ile kontrol et. Bazı proxy’ler X-Real-IP, bazıları True-Client-IP, CDN’ler ise kendi özel header’larını kullanır.
# Proxy'nin eklediği header'ları gör
curl -v http://your-backend-apache.internal/ 2>&1 | grep -i "x-forward|x-real|client-ip"
# Apache'ye gelen raw header'ları loglamak için
LogFormat "%{X-Forwarded-For}i %{X-Real-IP}i %h %l %u %t "%r" %>s" debug_headers
CustomLog /var/log/apache2/debug.log debug_headers
Virtual host çakışması: mod_remoteip direktifleri server seviyesinde veya yanlış virtual host içinde tanımlanmış olabilir. Her virtual host için ayrı ayrı kontrol et.
Cache veya reload sorunu: Apache systemctl reload ile konfigürasyonu yeniden yükler ama bazı köklü değişiklikler için tam restart gerekebilir:
sudo systemctl restart apache2
# RHEL tabanlı
sudo systemctl restart httpd
Sonuç
Reverse proxy arkasında çalışan Apache’de gerçek IP’yi loglamak kulağa basit gelebilir ama katmanlı mimarilerde, cloud ortamlarında ve farklı proxy teknolojileriyle çalışırken dikkat edilmesi gereken birçok nokta var. Temel adımlar şunlar:
mod_remoteipmodülünü aktif et- Sadece kendi kontrolündeki proxy IP’lerini trusted olarak tanımla
- Log formatını kontrol et ve gerekirse özelleştir
- Konfigürasyonu test et ve doğrula
- Güvenlik açısından trusted proxy listesini dar tut
Doğru yapılandırılmış bir kurulumla logların artık gerçek kullanıcı IP’lerini gösterecek. Bu sadece hata ayıklama için değil, güvenlik olayı müdahalesi, rate limiting, coğrafi kısıtlama ve anlamlı trafik analizi için de kritik öneme sahip. Bir de şunu belirteyim: bu konfigürasyonu yaptıktan sonra mevcut IP bazlı güvenlik kurallarını ve fail2ban gibi araçların konfigürasyonlarını da gözden geçir. Artık doğru IP’leri görüyorlar, o yüzden kurallarını buna göre ayarla.
