n8n Self-Hosted Güvenlik ve SSL Yapılandırması

Üretim ortamında n8n kurduktan sonra “şimdi bunu dünyaya açayım” diyorsunuz ve tam o noktada işler karmaşıklaşmaya başlıyor. SSL sertifikası, reverse proxy, kimlik doğrulama, network izolasyonu… Bunların hepsini doğru yapmazsanız, otomasyonlarınız güzel çalışsa bile sisteminiz bir güvenlik açığına dönüşüyor. Ben bu acıyı yaşadım. Şimdi size hem teorik hem de sahadan gelen pratik bilgileri aktaracağım.

n8n’i Neden Self-Host Ediyoruz ve Risk Nerede?

n8n cloud versiyonu rahat, biliyorum. Ama veri gizliliği, maliyet ve özelleştirme gereksinimleri nedeniyle pek çok şirket self-hosted tercih ediyor. Özellikle finans, sağlık veya e-ticaret sektöründeyseniz, iş akışlarınız API anahtarları, webhook URL’leri ve müşteri verileriyle dolu oluyor.

Self-hosted n8n’in tipik risk profili şöyle:

  • Açık port problemi: n8n varsayılan olarak 5678 portunda çalışır ve eğer firewall yapılandırılmamışsa internetten direkt erişilebilir hale gelir
  • HTTP üzerinden webhook: Şifreli olmayan bağlantıda webhook payload’ları açık geçer
  • Varsayılan kimlik doğrulama: n8n’in temel auth’u tek katmanlıdır, production için yeterli değildir
  • Sürüm açıkları: Self-hosted sistemlerde güncelleme ihmal edilir ve bilinen CVE’ler yamansız kalır

Şimdi bunları tek tek çözelim.

Temel Sistem Hazırlığı

Sunucunuza n8n kurmadan önce bazı temel güvenlik adımlarını atmanız gerekiyor. Ben genellikle Ubuntu 22.04 LTS üzerinde çalışıyorum, örnekler buna göre olacak.

# Sistemi güncelle
apt update && apt upgrade -y

# Gereksiz servisleri kapat
systemctl disable apache2 2>/dev/null
systemctl disable nginx 2>/dev/null  # n8n öncesi temiz başlangıç için

# UFW firewall konfigürasyonu
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# 5678 portu dışarıya KAPALIMALI
# ufw allow 5678 demeyin, bunu kasıtlı yazmıyorum

Şunu özellikle vurgulamak istiyorum: 5678 portunu asla doğrudan internete açmayın. Bu port sadece localhost’tan erişilebilir olmalı, dış trafik reverse proxy üzerinden gelecek.

Docker ile n8n Kurulumu ve Environment Güvenliği

n8n’i Docker ile çalıştırmak izolasyon açısından avantajlı. Ama environment variable’ları yanlış yönetirseniz sırlarınız log dosyalarına sızabilir.

Önce .env dosyasını oluşturun ve izinlerini kısıtlayın:

# .env dosyası oluştur
cat > /opt/n8n/.env << 'EOF'
# Temel ayarlar
N8N_HOST=0.0.0.0
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.sirketiniz.com/

# Güvenlik
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=BURAYA_GUCLU_PAROLA_YAZIN

# Encryption key - bu çok kritik!
N8N_ENCRYPTION_KEY=en_az_32_karakter_rastgele_string_buraya

# Veritabanı (SQLite yerine PostgreSQL öneriyorum)
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=localhost
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n_db
DB_POSTGRESDB_USER=n8n_user
DB_POSTGRESDB_PASSWORD=VERITABANI_PAROLASI

# Log güvenliği
N8N_LOG_LEVEL=warn
N8N_LOG_OUTPUT=console
EOF

# Dosya izinlerini kısıtla
chmod 600 /opt/n8n/.env
chown root:root /opt/n8n/.env

N8N_ENCRYPTION_KEY için şunu kullanabilirsiniz:

# Güçlü encryption key üret
openssl rand -hex 32

Bu anahtarı kaybederseniz credential’larınıza bir daha erişemezsiniz. Kesinlikle güvenli bir yere yedekleyin. HashiCorp Vault veya en azından şifreli bir password manager kullanın.

Nginx Reverse Proxy ve SSL Yapılandırması

Let’s Encrypt ile ücretsiz SSL sertifikası alacağız. Certbot ile bu işlem artık çok basit ama n8n için bazı özel ayarlar gerekiyor.

# Certbot kur
apt install certbot python3-certbot-nginx -y

# Nginx kur
apt install nginx -y

# Sertifika al (80 portu açık olmalı)
certbot certonly --nginx -d n8n.sirketiniz.com --email [email protected] --agree-tos

Şimdi Nginx konfigürasyonunu yazalım. Bu konfigürasyon production ortamda test edilmiş, websocket bağlantılarını da destekliyor:

# /etc/nginx/sites-available/n8n.conf

# HTTP'yi HTTPS'e yönlendir
server {
    listen 80;
    server_name n8n.sirketiniz.com;
    
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name n8n.sirketiniz.com;

    # SSL sertifika dosyaları
    ssl_certificate /etc/letsencrypt/live/n8n.sirketiniz.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.sirketiniz.com/privkey.pem;
    
    # Modern SSL ayarları
    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;
    ssl_session_tickets off;
    
    # 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-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    
    # n8n için büyük payload desteği (webhook'lar için)
    client_max_body_size 16M;
    
    # Proxy ayarları
    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        
        # WebSocket desteği - n8n 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ı (uzun süren iş akışları için)
        proxy_read_timeout 1800;
        proxy_connect_timeout 1800;
        proxy_send_timeout 1800;
    }
}
# Konfigürasyonu aktif et ve test et
ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

IP Bazlı Erişim Kısıtlaması

n8n arayüzüne sadece belirli IP’lerden erişilmesi gerekiyorsa, Nginx’e bunu ekleyebilirsiniz. Özellikle webhook endpoint’leri ile yönetim arayüzünü ayırt etmek akıllıca bir yaklaşım.

# Yönetim arayüzüne sadece ofis IP'den erişim
# /etc/nginx/sites-available/n8n.conf içindeki location / bloğunu değiştir

location / {
    # Ofis IP'si ve VPN subnet'i
    allow 203.0.113.10;
    allow 10.8.0.0/24;
    deny all;
    
    proxy_pass http://127.0.0.1:5678;
    # ... diğer proxy ayarları
}

# Webhook'lar herkese açık kalabilir
location /webhook/ {
    proxy_pass http://127.0.0.1:5678;
    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;
}

Bu yapılandırmayla n8n arayüzüne sadece tanımlı IP’lerden girebilirken, dış sistemler webhook’ları çağırabilir. Gerçek dünyada çok işe yarıyor bu ayrım.

Fail2Ban ile Brute Force Koruması

Basic auth açık olan bir servise dictionary attack gelebilir. Fail2ban bunu engellemek için iyi bir çözüm.

# Fail2ban kur
apt install fail2ban -y

# n8n için özel jail tanımı
cat > /etc/fail2ban/jail.d/n8n.conf << 'EOF'
[n8n]
enabled = true
filter = n8n
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 300
bantime = 3600
EOF

# n8n için filter oluştur
cat > /etc/fail2ban/filter.d/n8n.conf << 'EOF'
[Definition]
failregex = ^<HOST> .* "POST /rest/login HTTP/.*" 401
            ^<HOST> .* "GET .* HTTP/.*" 401
ignoreregex =
EOF

systemctl restart fail2ban
fail2ban-client status n8n

Systemd ile n8n Servisi ve Güvenli Çalışma Ortamı

Docker kullanmak istemiyorsanız veya Docker üzerine bir systemd service katmanı eklemek istiyorsanız, bu yapılandırma işe yarar. Özellikle NoNewPrivileges ve PrivateTmp gibi systemd güvenlik direktifleri n8n’i sandbox’a alır.

# /etc/systemd/system/n8n.service

[Unit]
Description=n8n - Workflow Automation
After=network.target postgresql.service
Wants=postgresql.service

[Service]
Type=simple
User=n8n
Group=n8n
WorkingDirectory=/opt/n8n
EnvironmentFile=/opt/n8n/.env

ExecStart=/usr/local/bin/n8n start
Restart=on-failure
RestartSec=5s

# Güvenlik kısıtlamaları
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/n8n/data

# Log
StandardOutput=journal
StandardError=journal
SyslogIdentifier=n8n

[Install]
WantedBy=multi-user.target
# n8n kullanıcısı oluştur (root ile çalıştırma!)
useradd -r -s /bin/false -d /opt/n8n n8n
mkdir -p /opt/n8n/data
chown -R n8n:n8n /opt/n8n

systemctl daemon-reload
systemctl enable n8n
systemctl start n8n

SSL Sertifika Yenileme Otomasyonu

Let’s Encrypt sertifikaları 90 günde bir yenilenmesi gerekiyor. Bunu unutursanız bir gün gelir webhook’larınız çalışmaz olur. Bunu certbot’un kendi timer’ına bırakmak yerine, yenileme sonrasında nginx’in de yeniden yükleneceğinden emin olun.

# Certbot renewal hook
cat > /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh << 'EOF'
#!/bin/bash
systemctl reload nginx
echo "$(date): SSL sertifikası yenilendi, nginx yeniden yüklendi" >> /var/log/certbot-renewal.log
EOF

chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh

# Manuel test
certbot renew --dry-run

# Cron yerine systemd timer kullanıyoruz
systemctl status certbot.timer

Webhook Güvenliği ve Secret Validation

n8n webhook’larına gelen isteklerin gerçekten beklediğiniz kaynaktan geldiğini doğrulamak önemli. n8n’in webhook node’unda “Header Auth” veya “Basic Auth” seçenekleri var ama bunların ötesinde, kaynak sistemden gelen imzayı doğrulamak daha güvenli.

GitHub webhook’ları için örnek bir yaklaşım:

# GitHub webhook secret'ını n8n'de kullanmak için
# Environment variable olarak tanımla
echo "GITHUB_WEBHOOK_SECRET=supersecretvalue123" >> /opt/n8n/.env

# n8n workflow içinde "Function" node ile doğrulama yapabilirsiniz:
# Bu JavaScript kodu n8n Function node'una yazılır:
// n8n Function Node - GitHub Webhook Doğrulama
const crypto = require('crypto');

const payload = JSON.stringify($input.first().json);
const signature = $input.first().headers['x-hub-signature-256'];
const secret = process.env.GITHUB_WEBHOOK_SECRET;

const expectedSignature = 'sha256=' + 
  crypto.createHmac('sha256', secret)
        .update(payload)
        .digest('hex');

if (signature !== expectedSignature) {
  throw new Error('Webhook imzası doğrulanamadı! Yetkisiz istek.');
}

return $input.all();

Bu yaklaşımı Stripe, Shopify ve diğer platformların webhook’ları için de adapte edebilirsiniz. Her platformun imza doğrulama mekanizması biraz farklı ama mantık aynı.

Günlük Güvenlik Kontrolleri ve Monitoring

Kurulduktan sonra “tamam artık güvende” diye kenara bırakmak yanlış. Basit bir günlük kontrol scripti:

#!/bin/bash
# /opt/scripts/n8n-security-check.sh

echo "=== n8n Güvenlik Kontrol Raporu - $(date) ==="

# Servis durumu
echo -e "n[1] Servis Durumu:"
systemctl is-active n8n && echo "n8n: ÇALIŞIYOR" || echo "n8n: DURDU - KONTROL ET!"
systemctl is-active nginx && echo "nginx: ÇALIŞIYOR" || echo "nginx: DURDU - KONTROL ET!"

# SSL sertifika geçerlilik kontrolü
echo -e "n[2] SSL Sertifika Durumu:"
CERT_EXPIRY=$(openssl s_client -connect n8n.sirketiniz.com:443 -servername n8n.sirketiniz.com 2>/dev/null | openssl x509 -noout -dates 2>/dev/null | grep notAfter | cut -d= -f2)
echo "Sertifika bitiş: $CERT_EXPIRY"

# Başarısız login denemeleri
echo -e "n[3] Son 24 Saatte Başarısız Login (401):"
grep "401" /var/log/nginx/access.log | grep -c "POST /rest/login"

# Fail2ban durumu
echo -e "n[4] Aktif Yasaklı IP'ler:"
fail2ban-client status n8n | grep "Banned IP"

# 5678 portu dışarıya açık mı kontrol
echo -e "n[5] Port 5678 Erişilebilirlik:"
ss -tlnp | grep 5678 | grep -q "127.0.0.1" && echo "GÜVENLI: Sadece localhost" || echo "UYARI: Dışarıya açık olabilir!"
chmod +x /opt/scripts/n8n-security-check.sh

# Her sabah 08:00'de çalıştır
echo "0 8 * * * root /opt/scripts/n8n-security-check.sh | mail -s 'n8n Güvenlik Raporu' [email protected]" >> /etc/crontab

n8n Güncelleme Stratejisi

Güvenliğin sürdürülmesi için düzenli güncelleme şart. Ama production’da “hemen güncelle” diyemezsiniz, workflow’larınız kırılabilir.

  • Staging önce: Önce test ortamında güncelleyin, workflow’larınızı test edin
  • Backup zorunlu: Güncelleme öncesi /opt/n8n/data dizinini ve PostgreSQL veritabanını yedekleyin
  • Release notes oku: Breaking change varsa önceden haberi olun, n8n’in changelog’u genellikle detaylı
  • Rollback planı: Hangi versiyona dönebileceğinizi ve nasıl döneceğinizi önceden planlayın
  • Maintenance window: Güncellemeyi mesai dışı yapın, birkaç dakika downtime olabilir
# Güncelleme öncesi backup
pg_dump -U n8n_user n8n_db > /backup/n8n_db_$(date +%Y%m%d).sql
cp -r /opt/n8n/data /backup/n8n_data_$(date +%Y%m%d)

# npm ile güncelleme
npm update -g n8n

# Docker kullanıyorsanız
docker pull n8nio/n8n:latest
docker-compose down && docker-compose up -d

Gerçek Hayat Senaryosu: Prodüksiyonda Yaşadığım Bir Sorun

Bir e-ticaret müşterisinde n8n kurmuştum. Her şey güzeldi, sertifika vardı, basic auth vardı. Birkaç ay sonra log’lara bakınca tuhaf bir şey fark ettim: aynı IP’den gelen yüzlerce webhook isteği, saniyede 50-100 tane. Bir bot webhook URL’sini keşfetmiş ve flood yapıyordu.

Çözüm Nginx rate limiting oldu:

# /etc/nginx/nginx.conf içine http bloğuna ekle
limit_req_zone $binary_remote_addr zone=webhook_limit:10m rate=10r/s;

# n8n.conf içinde webhook location bloğuna ekle
location /webhook/ {
    limit_req zone=webhook_limit burst=20 nodelay;
    limit_req_status 429;
    
    proxy_pass http://127.0.0.1:5678;
    # ... diğer ayarlar
}

Bu tek ekleme hem sunucu yükünü düşürdü hem de kötü niyetli flood girişimlerini engelledi. Şimdi her kurulumda baştan ekliyorum bunu.

Sonuç

n8n’i güvenli bir şekilde self-host etmek tek seferlik bir iş değil, sürekli bakım gerektiren bir süreç. Özetlemek gerekirse kritik adımlar şunlar:

  • 5678 portunu asla doğrudan internete açmayın, her zaman reverse proxy kullanın
  • SSL şart, HTTP üzerinde çalışan n8n production için kabul edilemez
  • Encryption key’i güvenli yerde saklayın, kaybederseniz tüm credential’larınız gider
  • Fail2ban ve rate limiting kurun, botlar her zaman gelir
  • Webhook imza doğrulaması yapın, URL’yi bilen herkes istek atabilir
  • Düzenli güncelleme ve backup rutini kurun, bunu sistemin parçası haline getirin
  • Monitoring scripti ile günlük kontrol yapın, sorunları erkenden yakalayın

Self-hosted çözümler özgürlük verir ama sorumluluk da getirir. Bu adımları atlamamak, ileride çok daha büyük baş ağrılarının önünü kesecektir.

Bir yanıt yazın

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