Apache mod_remoteip ile Gerçek IP Tespiti

Bir web sunucusu yönetirken en sinir bozucu durumlardan biri şudur: access log’larına bakıyorsunuz, tüm istekler aynı IP adresinden geliyor gibi görünüyor. Reverse proxy veya load balancer arkasında çalışan Apache kurulumlarında bu durum çok sık karşılaşılan bir problemdir. Gerçek kullanıcı IP’lerini görmek yerine proxy sunucunuzun IP’sini görürsünüz. İşte mod_remoteip tam da bu sorunu çözmek için var.

Sorun: Proxy Arkasındaki Apache Neden Yanlış IP Görür?

Klasik bir senaryo düşünelim. Önünüzde bir Nginx reverse proxy, arkasında Apache uygulama sunucusu var. Kullanıcı 203.0.113.42 IP adresinden sitenize giriyor. İstek önce Nginx’e geliyor, Nginx bunu Apache’ye iletirken bağlantıyı kendi adına açıyor. Apache, bağlantının karşı tarafı olarak Nginx’in IP adresini, örneğin 10.0.0.1‘i görüyor.

Bu durumda Apache’nin %h veya %a log değişkenleri 10.0.0.1 yazacak. Rate limiting, IP bazlı erişim kontrolü, coğrafi konum tespiti gibi her şey mahvolacak. Güvenlik açısından da sorunlu bir durum çünkü kötü niyetli bir kullanıcıyı IP bazında engelleyemezsiniz.

Nginx bu sorunu çözmek için isteğe X-Forwarded-For veya X-Real-IP gibi başlıklar ekler. Apache’nin bu başlıkları okuyup gerçek IP’yi tanıması için mod_remoteip modülünü aktifleştirmemiz gerekiyor.

mod_remoteip Nedir?

mod_remoteip, Apache 2.4 ile birlikte gelen ve mod_rpaf ile mod_extract_forwarded gibi eski modüllerin yerini alan resmi bir Apache modülüdür. Temel görevi şudur: belirli, güvenilir kaynaklardan gelen HTTP başlıklarını okuyarak useragent_ip değişkenini (yani Apache’nin “istemci IP” olarak gördüğü şeyi) güncellemek.

Modülün sağladığı temel avantajlar:

  • Gerçek kullanıcı IP tespiti: Log’larda, REMOTE_ADDR değişkeninde ve erişim kontrollerinde gerçek IP görünür
  • Güvenilir proxy zinciri yönetimi: Hangi proxy’lerin güvenilir olduğunu tanımlayabilirsiniz
  • Başlık esnekliği: X-Forwarded-For, X-Real-IP veya özel başlıklar kullanabilirsiniz
  • Birden fazla proxy desteği: CDN + Load Balancer + Reverse Proxy gibi karmaşık zincirleri destekler

Kurulum ve Aktivasyon

Debian/Ubuntu Sistemlerde

# mod_remoteip'i aktifleştir
sudo a2enmod remoteip

# Apache'yi yeniden başlat
sudo systemctl restart apache2

# Modülün yüklendiğini doğrula
apache2ctl -M | grep remoteip

Çıktıda remoteip_module (shared) görüyorsanız modül başarıyla yüklenmiştir.

RHEL/CentOS/AlmaLinux Sistemlerde

Red Hat tabanlı sistemlerde modül genellikle Apache ile birlikte zaten gelir, sadece yapılandırma dosyasını oluşturmanız yeterlidir:

# Modülün mevcut olduğunu kontrol et
ls -la /usr/lib64/httpd/modules/mod_remoteip.so

# httpd.conf veya conf.d altına yapılandırma ekle
sudo vi /etc/httpd/conf.d/remoteip.conf

Modül yükleme satırı genelde httpd.conf içinde zaten bulunur ama yoksa şunu ekleyin:

# /etc/httpd/conf.modules.d/00-base.conf içinde şu satırın olduğunu kontrol edin
LoadModule remoteip_module modules/mod_remoteip.so

Temel Yapılandırma

En basit senaryo: tek bir reverse proxy arkasında Apache çalışıyor.

# /etc/apache2/conf-available/remoteip.conf (Ubuntu)
# veya /etc/httpd/conf.d/remoteip.conf (RHEL)

LoadModule remoteip_module modules/mod_remoteip.so

# Kullanılacak başlık
RemoteIPHeader X-Forwarded-For

# Güvenilir proxy IP'leri (bu IP'lerden gelen başlıklara inanılır)
RemoteIPTrustedProxy 10.0.0.1
RemoteIPTrustedProxy 10.0.0.2

# Alternatif: Bir subnet tanımlayabilirsiniz
RemoteIPTrustedProxy 10.0.0.0/24
# Ubuntu'da conf dosyasını etkinleştir
sudo a2enconf remoteip
sudo systemctl reload apache2

Kritik nokta: RemoteIPTrustedProxy direktifini dikkatli kullanın. Buraya eklediğiniz IP’lerden gelen X-Forwarded-For başlıklarına körü körüne inanılır. Yanlış yapılandırma, kullanıcıların istedikleri IP’yi taklit etmesine yol açabilir.

Log Formatını Güncelleme

mod_remoteip devreye girdiğinde %a değişkeni artık gerçek kullanıcı IP’sini döndürür. Ancak log formatınızı da güncellemeniz gerekebilir:

# Virtual host veya genel log yapılandırması
LogFormat "%a %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined_realip

# Hem gerçek IP hem proxy IP'sini loglamak için
LogFormat "%a (proxy: %{c}a) %l %u %t "%r" %>s %O" proxy_debug

CustomLog ${APACHE_LOG_DIR}/access.log combined_realip

Burada iki önemli değişken var:

  • %a: mod_remoteip tarafından set edilen gerçek istemci IP’si
  • %{c}a: Fiziksel bağlantının IP’si (yani proxy’nin IP’si)

Bu ikisini birlikte loglamak, troubleshooting sırasında çok işe yarar.

Gerçek Dünya Senaryoları

Senaryo 1: Nginx + Apache Stack

En yaygın kurulum budur. Nginx önde SSL termination ve statik dosya servisi yapıyor, Apache arkada PHP uygulamasını çalıştırıyor.

Önce Nginx tarafında başlığın doğru set edildiğinden emin olun:

# /etc/nginx/conf.d/app.conf
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

Ardından Apache tarafında:

# /etc/apache2/conf-available/remoteip.conf
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 127.0.0.1

# VirtualHost yapılandırması
<VirtualHost *:8080>
    ServerName example.com
    DocumentRoot /var/www/html

    LogFormat "%a %l %u %t "%r" %>s %O" combined_realip
    CustomLog /var/log/apache2/access.log combined_realip

    # PHP veya uygulama burada çalışıyor
</VirtualHost>

Senaryo 2: Cloudflare CDN Arkasında Apache

Cloudflare kullanıyorsanız, trafik Cloudflare’in IP adresleri üzerinden gelir. Cloudflare gerçek kullanıcı IP’sini CF-Connecting-IP başlığında iletir veya X-Forwarded-For‘a ekler.

# Cloudflare IP aralıkları (güncel listeyi Cloudflare dokümantasyonundan alın)
RemoteIPHeader CF-Connecting-IP

RemoteIPTrustedProxy 103.21.244.0/22
RemoteIPTrustedProxy 103.22.200.0/22
RemoteIPTrustedProxy 103.31.4.0/22
RemoteIPTrustedProxy 104.16.0.0/13
RemoteIPTrustedProxy 104.24.0.0/14
RemoteIPTrustedProxy 108.162.192.0/18
RemoteIPTrustedProxy 131.0.72.0/22
RemoteIPTrustedProxy 141.101.64.0/18
RemoteIPTrustedProxy 162.158.0.0/15
RemoteIPTrustedProxy 172.64.0.0/13
RemoteIPTrustedProxy 173.245.48.0/20
RemoteIPTrustedProxy 188.114.96.0/20
RemoteIPTrustedProxy 190.93.240.0/20
RemoteIPTrustedProxy 197.234.240.0/22
RemoteIPTrustedProxy 198.41.128.0/17

# IPv6 için
RemoteIPTrustedProxy 2400:cb00::/32
RemoteIPTrustedProxy 2606:4700::/32

Önemli not: Cloudflare IP listesi zaman zaman değişir. Bunu script ile otomatik güncellemek iyi bir pratiktir:

#!/bin/bash
# /usr/local/bin/update_cloudflare_ips.sh

CF_IPS_FILE="/etc/apache2/conf-available/cloudflare_ips.conf"
TEMP_FILE=$(mktemp)

echo "# Cloudflare IP Ranges - $(date)" > $TEMP_FILE
echo "RemoteIPHeader CF-Connecting-IP" >> $TEMP_FILE
echo "" >> $TEMP_FILE

# IPv4
curl -s https://www.cloudflare.com/ips-v4 | while read ip; do
    echo "RemoteIPTrustedProxy $ip" >> $TEMP_FILE
done

# IPv6
curl -s https://www.cloudflare.com/ips-v6 | while read ip; do
    echo "RemoteIPTrustedProxy $ip" >> $TEMP_FILE
done

mv $TEMP_FILE $CF_IPS_FILE
apache2ctl configtest && systemctl reload apache2
# Script'i çalıştırılabilir yap ve cron'a ekle
chmod +x /usr/local/bin/update_cloudflare_ips.sh

# Haftada bir güncelle
echo "0 3 * * 0 root /usr/local/bin/update_cloudflare_ips.sh" >> /etc/cron.d/cloudflare-ips

Senaryo 3: HAProxy + Apache Load Balancer Kurulumu

Büyük ölçekli kurulumda HAProxy önde, birden fazla Apache backend sunucusu arkada olabilir:

# HAProxy'nin IP'sini güvenilir olarak işaretle
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 192.168.1.10
RemoteIPTrustedProxy 192.168.1.11

# HAProxy'nin proxy protocol kullanması durumunda
# (daha güvenli bir yöntem)
RemoteIPProxyProtocol On

HAProxy tarafında PROXY protocol kullanımı önerilir çünkü başlık manipülasyonuna karşı daha dayanıklıdır:

# haproxy.cfg
frontend web_frontend
    bind *:80
    default_backend apache_backends

backend apache_backends
    option forwardfor
    server apache1 192.168.1.20:80 send-proxy
    server apache2 192.168.1.21:80 send-proxy

IP Bazlı Erişim Kontrolü ile Birlikte Kullanım

mod_remoteip devredeyken Require ip direktifi gerçek IP’ye göre çalışır:

<VirtualHost *:80>
    ServerName admin.example.com
    DocumentRoot /var/www/admin

    # Admin paneline sadece belirli IP'lerden erişim
    <Directory /var/www/admin>
        Require ip 203.0.113.0/24
        Require ip 198.51.100.42
    </Directory>
</VirtualHost>

Proxy arkasında mod_remoteip olmadan bu erişim kontrolü çalışmaz, çünkü Apache proxy IP’sini görür. Modül aktif olduğunda gerçek kullanıcı IP’sine göre kısıtlama yapabilirsiniz.

mod_rewrite ile Birlikte Kullanım

%{REMOTE_ADDR} değişkeni de mod_remoteip tarafından güncellenir. Bu sayede URL rewrite kurallarında gerçek IP’yi kullanabilirsiniz:

<VirtualHost *:80>
    RewriteEngine On

    # Belirli IP'leri maintenance sayfasına yönlendirme (gerçek IP'ye göre)
    RewriteCond %{REMOTE_ADDR} !^203.0.113.
    RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f
    RewriteRule ^(.*)$ /maintenance.html [L]

    # Coğrafi konum bazlı yönlendirme için IP loglama
    RewriteRule ^ - [E=CLIENT_IP:%{REMOTE_ADDR}]
</VirtualHost>

PHP Uygulamalarında Gerçek IP

mod_remoteip aktif olduğunda PHP’de $_SERVER['REMOTE_ADDR'] otomatik olarak gerçek IP’yi döndürür. Artık $_SERVER['HTTP_X_FORWARDED_FOR']‘a bakmanıza gerek yok:

<?php
// mod_remoteip aktifken bu yeterli
$real_ip = $_SERVER['REMOTE_ADDR'];

// Doğrulama için her ikisini de loglayabilirsiniz
error_log("Real IP: " . $_SERVER['REMOTE_ADDR']);
error_log("Forwarded For: " . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? 'N/A'));
?>

Bu çok önemli bir avantaj. Uygulama kodunuzda proxy farkındalığı gerektiren karmaşık IP tespit mantığı yazmanıza gerek kalmaz.

Güvenlik Dikkat Noktaları

Proxy Zinciri Doğrulama

RemoteIPTrustedProxy ile RemoteIPInternalProxy direktifleri arasındaki farkı anlamak önemlidir:

  • RemoteIPTrustedProxy: Bu IP’lerden gelen başlıklara güvenilir, bu IP’ler log’da görünür
  • RemoteIPInternalProxy: Bu IP’lerden gelen başlıklara güvenilir, bu IP’ler log’da görünmez (internal kabul edilir)
# Harici güvenilir proxy (log'da görünür)
RemoteIPTrustedProxy 203.0.113.1

# Dahili proxy (log'da görünmez, internal network)
RemoteIPInternalProxy 10.0.0.0/8
RemoteIPInternalProxy 172.16.0.0/12
RemoteIPInternalProxy 192.168.0.0/16

IP Spoofing Riski

Eğer RemoteIPTrustedProxy yanlış yapılandırılırsa, herhangi bir kullanıcı X-Forwarded-For: 1.2.3.4 başlığı göndererek istediği IP’yi taklit edebilir. Bu yüzden:

# Test: Dışarıdan sahte başlık gönderme denemesi
curl -H "X-Forwarded-For: 1.2.3.4" http://your-apache-server/

# Log'da 1.2.3.4 görünüyorsa SORUN VAR demektir
# Eğer gerçek IP görünüyorsa, yapılandırma doğrudur

Sadece gerçekten güvendiğiniz proxy sunucularının IP’lerini RemoteIPTrustedProxy listesine ekleyin. Asla 0.0.0.0/0 veya geniş subnet’ler kullanmayın.

Yapılandırmayı Test Etme

# Syntax kontrolü
sudo apache2ctl configtest

# Reload
sudo systemctl reload apache2

# Gerçek IP'nin log'a düştüğünü doğrula
tail -f /var/log/apache2/access.log

# Başka bir terminalden test isteği gönder
curl -v http://your-domain.com/

# Proxy simülasyonu yaparak test
curl -H "X-Forwarded-For: 203.0.113.100" 
     --interface lo 
     http://localhost/test

Apache’nin modülü doğru yükleyip yüklemediğini ve hangi direktifleri gördüğünü kontrol etmek için:

# Tüm aktif modülleri listele
apache2ctl -M | grep -E "remoteip|proxy"

# Yapılandırma dosyasında remoteip ayarlarını ara
grep -r "RemoteIP" /etc/apache2/

# Apache'nin birleştirilmiş yapılandırmasını görüntüle
apache2ctl -S

Yaygın Sorunlar ve Çözümleri

Sorun: Log’lar hala proxy IP’sini gösteriyor

Önce modülün gerçekten yüklendiğini doğrulayın. Sonra RemoteIPHeader direktifindeki başlık adının proxy’nin gönderdiği başlıkla birebir eşleştiğini kontrol edin. Nginx X-Forwarded-For gönderirken siz X-Real-IP dinliyor olabilirsiniz.

# Proxy'nin hangi başlıkları gönderdiğini gör
# Apache'ye gelen başlıkları dump eden geçici bir endpoint
# .htaccess veya VirtualHost içine:
# SetEnvIf Request_URI "^/headers$" DUMP_HEADERS=1
# Header always set X-Debug-Remote-Addr %{REMOTE_ADDR}e

Sorun: PHP hala eski IP’yi görüyor

Apache yeniden başlatılmamış olabilir. systemctl reload yerine systemctl restart deneyin. Ayrıca PHP-FPM kullanıyorsanız onu da yeniden başlatın.

Sorun: mod_security veya fail2ban gerçek IP’yi tanımıyor

mod_remoteip işledikten sonra diğer modüller doğru IP’yi görür, ancak fail2ban log’lardan IP okur. Log formatınızda %a kullandığınızdan ve fail2ban’ın bu formatı parse ettiğinden emin olun.

Sonuç

mod_remoteip, reverse proxy veya CDN arkasında çalışan her Apache kurulumu için adeta zorunlu bir yapılandırmadır. Olmadığında güvenlik loglarınız anlamsız, IP bazlı erişim kontrolleriniz işlevsiz, rate limiting ve coğrafi kısıtlamalarınız çöp olur.

Yapılandırması görece basit olmakla birlikte güvenlik açısından dikkatli olmak gerekiyor. RemoteIPTrustedProxy listesini minimal tutun, sadece gerçekten güvendiğiniz sunucuları ekleyin ve periyodik olarak kontrol edin. Cloudflare gibi harici servisler kullanıyorsanız IP listelerini güncel tutmak için otomatik bir mekanizma kurun.

Modülü devreye aldıktan sonra her zaman test edin: log’larda gerçek kullanıcı IP’lerini görüyor musunuz, IP bazlı erişim kontrolleriniz doğru çalışıyor mu, PHP uygulamanız REMOTE_ADDR‘den doğru IP alıyor mu? Bu üç kontrol tamam ise sisteminiz sağlıklı çalışıyor demektir.

Son olarak, %{c}a ile hem gerçek IP hem proxy IP’sini birlikte loglamayı alışkanlık edinin. Bir şeyler ters gittiğinde bu log formatı troubleshooting sürenizi ciddi ölçüde kısaltır.

Yorum yapın