Uptime Kuma Güvenlik ve Reverse Proxy Yapılandırması

Uptime Kuma’yı kurup çalıştırmak aslında çok kolay. Bir Docker komutu, birkaç tıklama ve izleme sisteminiz hazır. Ama o noktada durup “tamam, bu şeyi güvenli hale nasıl getiririm?” diye sormayan çok sistem yöneticisi gördüm. Çünkü iş yapıyor, monitörler yeşil, kimse sormadan bırakıyorsunuz. Sonra bir gün logları karıştırırken Çin’den, Rusya’dan, kim bilir nereden brute force denemeleri görüyorsunuz. Bu yazıda Uptime Kuma’yı production ortamında gerçekten güvenli şekilde çalıştırmak için ne yapmanız gerektiğini aktaracağım.

Neden Uptime Kuma Güvenliği Önemli?

Uptime Kuma bir monitoring aracı, yani doğası gereği çok sayıda servis hakkında bilgi tutuyor. API endpoint’leriniz, veritabanı bağlantı bilgileriniz, internal servislerinizin URL’leri, hatta bazı durumlarda credential içeren health check URL’leri. Bunların hepsi Uptime Kuma’nın veritabanında duruyor.

Buna ek olarak, Uptime Kuma varsayılan kurulumda HTTP üzerinden 3001 portunda çalışıyor. Eğer firewall kurallarınız gevşekse ya da “sadece monitoring aracı, ne olacak ki” mantığıyla internete açık bıraktıysanız, bu bir saldırgan için oldukça ilgi çekici bir hedef. Özellikle statuspage özelliğini kullanıyorsanız, yanlış yapılandırılmış bir kurulumda internal altyapınız hakkında fazla bilgi sızdırabilirsiniz.

Temel Güvenlik Katmanları

Uptime Kuma güvenliğini katmanlı düşünmek lazım. Tek bir şeyi yapıp geçmek yetmiyor. Ben bunu şöyle ele alıyorum:

  • Network seviyesi: Hangi portlar açık, kimler erişebilir
  • Uygulama seviyesi: Authentication, session yönetimi
  • Transport seviyesi: SSL/TLS şifreleme
  • Reverse proxy seviyesi: Rate limiting, header güvenliği, ek auth katmanları

Şimdi bunları tek tek inceleyelim.

Firewall ile Port Güvenliği

İlk adım basit ama çok önemli. Uptime Kuma’nın 3001 portunu doğrudan internete açık bırakmayın. Eğer UFW kullanıyorsanız:

# 3001 portunu dışarıya kapatın
sudo ufw deny 3001

# Sadece localhost'tan erişime izin verin (reverse proxy için)
sudo ufw allow from 127.0.0.1 to any port 3001

# Eğer Docker ağı üzerinden erişiyorsanız Docker bridge subnet'ini ekleyin
sudo ufw allow from 172.17.0.0/16 to any port 3001

Dikkat edin, Docker kullanıyorsanız UFW kuralları her zaman beklendiği gibi çalışmıyor. Docker kendi iptables kurallarını yazıyor ve UFW’u atlayabiliyor. Bu yüzden Docker ile çalışıyorsanız port binding’e de dikkat etmek gerekiyor.

Docker Compose dosyanızda şöyle bir ayrım var:

# YANLIŞ - tüm interface'lere bind eder
ports:
  - "3001:3001"

# DOĞRU - sadece localhost'a bind eder
ports:
  - "127.0.0.1:3001:3001"

Bu küçük fark, konteynerinizin dışarıdan doğrudan erişilebilir olup olmaması arasındaki farktır. Production’da her zaman 127.0.0.1:3001:3001 kullanın.

Nginx ile Reverse Proxy Kurulumu

Nginx, Uptime Kuma için en yaygın kullanılan reverse proxy. WebSocket desteği kritik çünkü Uptime Kuma arayüzü gerçek zamanlı güncellemeler için WebSocket kullanıyor. Bunu unutup standart bir proxy yapılandırması yaparsanız arayüz düzgün çalışmaz.

server {
    listen 80;
    server_name uptime.example.com;
    
    # HTTP'yi HTTPS'e yönlendir
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name uptime.example.com;

    # SSL sertifikaları (Let's Encrypt örneği)
    ssl_certificate /etc/letsencrypt/live/uptime.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/uptime.example.com/privkey.pem;

    # Modern SSL yapılandırması
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # Güvenlik header'ları
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' wss:;" always;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        
        # WebSocket desteği için kritik
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        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;
        
        # Timeout ayarları
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        
        # Buffer ayarları
        proxy_buffering off;
    }
}

WebSocket için proxy_read_timeout değerini yüksek tutmanız önemli. Varsayılan 60 saniye, bu süre dolunca WebSocket bağlantısı kopuyor ve arayüz sürekli yeniden bağlanmaya çalışıyor.

Caddy ile Reverse Proxy Alternatifi

Nginx konfigürasyonu karmaşık geliyorsa Caddy iyi bir alternatif. Otomatik SSL sertifikası yönetimiyle birlikte gelir ve Uptime Kuma için gereken WebSocket desteği varsayılan olarak aktif.

uptime.example.com {
    reverse_proxy 127.0.0.1:3001 {
        # WebSocket için flush interval
        flush_interval -1
    }
    
    # Header güvenliği
    header {
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        Referrer-Policy "strict-origin-when-cross-origin"
        -Server
    }
    
    # Rate limiting (Caddy Enterprise gerektirir, basic rate limit için)
    # Açık kaynak için fail2ban ile birleştirin
}

Caddy’nin güzel yanı -Server header’ını kaldırabilmeniz. Bu küçük detay saldırganlara hangi sunucu yazılımını kullandığınızı söylemez.

Ek Kimlik Doğrulama Katmanı: HTTP Basic Auth

Uptime Kuma’nın kendi authentication sistemi var ama buna ek olarak reverse proxy seviyesinde bir auth katmanı eklemek savunma derinliği sağlar. Özellikle Uptime Kuma’nın kendisinde sıfır gün açığı çıksa bile bir koruma katmanınız daha oluyor.

Nginx için htpasswd dosyası oluşturun:

# apache2-utils paketini yükleyin
sudo apt install apache2-utils

# Kullanıcı oluşturun
sudo htpasswd -c /etc/nginx/.htpasswd uptime_admin

# İkinci kullanıcı eklemek için -c olmadan çalıştırın
sudo htpasswd /etc/nginx/.htpasswd second_user

# Dosya izinlerini ayarlayın
sudo chmod 640 /etc/nginx/.htpasswd
sudo chown root:www-data /etc/nginx/.htpasswd

Nginx konfigürasyonuna ekleyin:

location / {
    # Ek authentication katmanı
    auth_basic "Uptime Kuma - Restricted Access";
    auth_basic_user_file /etc/nginx/.htpasswd;
    
    proxy_pass http://127.0.0.1:3001;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    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;
    proxy_read_timeout 3600s;
    proxy_buffering off;
}

Burada şunu belirtmeliyim: Eğer statuspage özelliğini kullanıyorsanız ve bu sayfanın kamuya açık olmasını istiyorsanız, sadece /status/ path’ini auth dışında tutabilirsiniz.

IP Kısıtlaması ile Erişim Kontrolü

Eğer Uptime Kuma’ya sadece belirli IP adreslerinden erişilmesi gerekiyorsa, bunu nginx seviyesinde yapabilirsiniz:

server {
    listen 443 ssl http2;
    server_name uptime.example.com;

    # SSL ve diğer ayarlar...

    # VPN ya da ofis IP adresleri
    set $allowed_ip 0;
    
    if ($remote_addr = "203.0.113.10") {
        set $allowed_ip 1;
    }
    if ($remote_addr = "198.51.100.25") {
        set $allowed_ip 1;
    }
    
    # Statuspage herkese açık olabilir
    location /status/ {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }

    location / {
        # IP kontrolü
        if ($allowed_ip = 0) {
            return 403;
        }
        
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
        proxy_read_timeout 3600s;
        proxy_buffering off;
    }
}

Çalıştığınız yerde ofis VPN’i varsa bu yaklaşım çok temiz çalışıyor. Monitoring paneline sadece VPN üzerinden erişilebiliyor, statuspage ise herkesin görebileceği şekilde açık kalıyor.

Rate Limiting

Brute force saldırılarına karşı rate limiting eklemek şart. Nginx’te bunu şöyle yapabilirsiniz:

http {
    # Rate limit zone tanımı - ip başına 10 istek/dakika
    limit_req_zone $binary_remote_addr zone=uptime_limit:10m rate=10r/m;
    
    server {
        # ...
        
        location / {
            # Rate limiting uygula, 20'ye kadar burst izin ver
            limit_req zone=uptime_limit burst=20 nodelay;
            limit_req_status 429;
            
            proxy_pass http://127.0.0.1:3001;
            # diğer proxy ayarları...
        }
    }
}

Login endpoint’ine daha sıkı limit koymak isteyebilirsiniz:

http {
    limit_req_zone $binary_remote_addr zone=uptime_login:10m rate=5r/m;
    
    server {
        location /api/login {
            limit_req zone=uptime_login burst=5 nodelay;
            limit_req_status 429;
            
            proxy_pass http://127.0.0.1:3001;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
        }
    }
}

Fail2ban Entegrasyonu

Nginx rate limiting iyidir ama fail2ban ile birleştirince çok daha güçlü olur. Başarısız login denemelerini otomatik olarak bloke edebilirsiniz.

Önce nginx log formatını ayarlayın:

http {
    log_format uptime_log '$remote_addr - $remote_user [$time_local] '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent"';

    server {
        access_log /var/log/nginx/uptime-access.log uptime_log;
        error_log /var/log/nginx/uptime-error.log;
    }
}

Fail2ban filter oluşturun:

sudo nano /etc/fail2ban/filter.d/uptime-kuma.conf
[Definition]
failregex = ^<HOST> .* "POST /api/login HTTP.*" 401
ignoreregex =

Jail yapılandırması:

sudo nano /etc/fail2ban/jail.d/uptime-kuma.conf
[uptime-kuma]
enabled = true
port = http,https
filter = uptime-kuma
logpath = /var/log/nginx/uptime-access.log
maxretry = 5
findtime = 300
bantime = 3600
action = iptables-multiport[name=uptime, port="http,https"]

Fail2ban’ı yeniden başlatın:

sudo systemctl restart fail2ban

# Durumu kontrol edin
sudo fail2ban-client status uptime-kuma

Bu yapılandırmayla, 5 dakika içinde 5 başarısız giriş denemesi yapan IP 1 saat boyunca bloke ediliyor.

Uptime Kuma Uygulama Güvenliği Ayarları

Reverse proxy ve network güvenliğinin yanında, Uptime Kuma’nın kendi ayarlarını da doğru yapılandırmak gerekiyor.

Güçlü şifre kullanımı: Kurulum sırasında belirlediğiniz admin şifresi kritik. Minimum 16 karakter, büyük-küçük harf, sayı ve özel karakter içermeli. “admin123” veya hostname’iniz şifre olmasın.

Disable Registration: Eğer multi-user özelliğini kullanmıyorsanız yeni kullanıcı kaydını kapatın. Settings > Advanced > Disable Registration seçeneğini aktive edin.

API Key güvenliği: Uptime Kuma API key’lerini harici araçlarla entegre ederken bu key’leri ortam değişkeni olarak saklayın, config dosyasına yazmayın.

Veri yedekleme ve şifreleme: Uptime Kuma veritabanı (kuma.db) hassas bilgiler içerebilir. Bu dosyayı yedeklerken şifreli saklayın:

# Veritabanını şifreli yedekle
gpg --symmetric --cipher-algo AES256 
    --output /backup/kuma-$(date +%Y%m%d).db.gpg 
    /opt/uptime-kuma/data/kuma.db

# Yedek kontrolü
gpg --decrypt /backup/kuma-20240115.db.gpg | file -

Docker Environment Güvenliği

Eğer Docker ile çalışıyorsanız, production Docker Compose dosyanız şöyle görünmeli:

version: '3.8'

services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    
    # Kritik: sadece localhost'a bind et
    ports:
      - "127.0.0.1:3001:3001"
    
    volumes:
      - uptime-kuma-data:/app/data
    
    # Güvenlik kısıtlamaları
    security_opt:
      - no-new-privileges:true
    
    # Read-only root filesystem (veri dizini hariç)
    # read_only: true  # Uptime Kuma bunu tam desteklemeyebilir, test edin
    
    # Capabilities kısıtla
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    
    # Kaynak limitleri
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
        reservations:
          memory: 64M
    
    restart: unless-stopped
    
    environment:
      - TZ=Europe/Istanbul
      # UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN=false # Sadece gerekirse

volumes:
  uptime-kuma-data:
    driver: local

no-new-privileges:true özellikle önemli. Bu, konteyner içindeki bir sürecin setuid/setgid ile privilege escalation yapmasını engelliyor.

SSL Sertifika Otomasyonu

Let’s Encrypt sertifikalarınızın otomatik yenilenmesi için bir cron job veya systemd timer kurun. Certbot kullanıyorsanız:

# Mevcut sertifika yenileme testini çalıştırın
sudo certbot renew --dry-run

# Otomatik yenileme için cron (zaten kurulmuş olmalı ama kontrol edin)
sudo crontab -l | grep certbot

# Yenileme sonrası nginx'i reload eden hook
sudo nano /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
echo "$(date): Nginx reloaded after certificate renewal" >> /var/log/certbot-nginx-reload.log
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh

Güvenlik Denetimi ve Log İzleme

Yapılandırmanızı tamamladıktan sonra düzenli olarak kontrol etmeniz gereken birkaç şey var.

Nginx loglarını analiz edin:

# Son 24 saatte en çok istek atan IP'ler
awk '{print $1}' /var/log/nginx/uptime-access.log | 
    sort | uniq -c | sort -rn | head -20

# 4xx ve 5xx hataları
grep -E '" (4|5)[0-9]{2} ' /var/log/nginx/uptime-access.log | 
    awk '{print $9}' | sort | uniq -c | sort -rn

# Login denemelerini izle
grep "POST /api/login" /var/log/nginx/uptime-access.log | 
    grep -v '" 200 ' | awk '{print $1}' | sort | uniq -c | sort -rn

SSL yapılandırmanızı test edin:

# SSL Labs benzeri test için testssl.sh
bash testssl.sh uptime.example.com

# Ya da online: https://www.ssllabs.com/ssltest/
# Minimum A rating hedefleyin, A+ için HSTS preload gerekli

Statuspage Güvenliği

Uptime Kuma’nın statuspage özelliğini kullanıyorsanız, neyi gösterdiğinize dikkat edin. Statuspage üzerinde görünen monitor isimleri ve URL’ler hakkında dikkatli olun.

  • Internal servis adlarını statuspage’de direkt göstermeyin (örneğin “192.168.1.50:8080 PostgreSQL” yerine “Veritabanı Servisi” yazın)
  • Statuspage için ayrı bir domain veya subdomain kullanın
  • Statuspage’i nginx’te ayrı bir location block ile yönetin, böylece admin arayüzüne erişimi kısıtlarken statuspage’i açık tutabilirsiniz

Sonuç

Uptime Kuma’yı güvenli çalıştırmak rocket science değil ama “sadece çalışıyor” aşamasında bırakmak da olmuyor. Özetlemek gerekirse en kritik adımlar şunlar:

  • 3001 portunu asla doğrudan internete açmayın, her zaman reverse proxy kullanın
  • Docker port binding’inde 127.0.0.1:3001:3001 kullanın
  • WebSocket desteği için nginx yapılandırmanızda Upgrade header’larını unutmayın
  • SSL/TLS zorunlu, TLS 1.2 minimum, TLS 1.3 tercih edilmeli
  • Rate limiting ve fail2ban brute force saldırılarına karşı çift katman koruma sağlıyor
  • Güvenlik header’ları modern browser güvenliği için şart
  • Veri yedeklerini şifreli saklayın

Bunları bir kere doğru yapıp bir script veya Ansible playbook’a dökerseniz, yeni kurulumlarda aynı süreci otomatik olarak uygulayabilirsiniz. Ben bu yapılandırmayı bir Ansible rolüne çevirdim, her yeni sunucuda 10 dakika içinde güvenli Uptime Kuma kurulumu tamamlanıyor. Siz de yapabilirsiniz.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir