Uzak Bağlantı Güvenliği: MySQL SSL Yapılandırması

Veritabanı sunucularına olan uzak bağlantılar, doğru yapılandırılmadığında ciddi güvenlik açıkları doğurur. Özellikle MySQL sunucunuz farklı ağ segmentlerindeki uygulama sunucularıyla konuşuyorsa, bu trafiğin şifrelenmemesi sizi man-in-the-middle saldırılarına, kimlik bilgisi çalmaya ve veri sızıntısına açık hale getirir. Bu yazıda MySQL SSL yapılandırmasını baştan sona, gerçek dünya senaryolarıyla ele alacağız.

Neden SSL/TLS Şifrelemesi Zorunlu?

Çoğu sysadmin MySQL’i kurar, bind-address ayarını yapar ve işi bitmiş sayar. Ama şöyle düşün: uygulama sunucun ile veritabanı sunucun aynı veri merkezinde olsa bile, aynı switch üzerindeki başka bir makine ele geçirildiğinde o iç ağ trafiği dinlenebilir. ARP spoofing saldırısıyla ağ trafiğini yönlendirip açık metin MySQL paketlerini yakalamak, doğru araçlarla birkaç dakika meselesidir.

Bunu bir kez yaşadıktan sonra SSL atlamak artık bir seçenek olmaktan çıkıyor. PCI-DSS, HIPAA gibi uyumluluk standartları da veritabanı bağlantılarında şifrelemeyi zorunlu kılıyor.

Sertifika Altyapısını Hazırlamak

Ticari bir CA’ya para vermene gerek yok. MySQL için kendi Certificate Authority’ni oluşturacağız. Bu yapıda üç ayrı sertifika setine ihtiyacımız var:

  • CA sertifikası: Hem sunucu hem istemci sertifikalarını imzalar
  • Sunucu sertifikası: MySQL sunucusunun kimliğini kanıtlar
  • İstemci sertifikası: İstemcinin sunucuya kimliğini kanıtlar (mutual TLS)

Sertifikaları üretmek için bir dizin oluşturup işe başlayalım:

mkdir -p /etc/mysql/ssl
cd /etc/mysql/ssl

# CA anahtar ve sertifikası
openssl genrsa 4096 > ca-key.pem
openssl req -new -x509 -nodes -days 3650 
  -key ca-key.pem 
  -out ca-cert.pem 
  -subj "/C=TR/ST=Istanbul/L=Istanbul/O=SirketAdi/CN=MySQL-CA"

# Sunucu anahtarı ve CSR
openssl req -newkey rsa:4096 -nodes 
  -keyout server-key.pem 
  -out server-req.pem 
  -subj "/C=TR/ST=Istanbul/L=Istanbul/O=SirketAdi/CN=db.sirket.local"

# Sunucu sertifikasını CA ile imzala
openssl x509 -req -days 3650 
  -in server-req.pem 
  -CA ca-cert.pem 
  -CAkey ca-key.pem 
  -CAcreateserial 
  -out server-cert.pem

# İstemci anahtarı ve CSR
openssl req -newkey rsa:4096 -nodes 
  -keyout client-key.pem 
  -out client-req.pem 
  -subj "/C=TR/ST=Istanbul/L=Istanbul/O=SirketAdi/CN=app-client"

# İstemci sertifikasını CA ile imzala
openssl x509 -req -days 3650 
  -in client-req.pem 
  -CA ca-cert.pem 
  -CAkey ca-key.pem 
  -CAcreateserial 
  -out client-cert.pem

Sertifikaları doğrulayalım, özellikle CN alanlarının doğru olduğundan emin ol:

openssl verify -CAfile ca-cert.pem server-cert.pem
openssl verify -CAfile ca-cert.pem client-cert.pem

# Sertifika detaylarını görüntüle
openssl x509 -in server-cert.pem -text -noout | grep -A 2 "Subject:"
openssl x509 -in server-cert.pem -text -noout | grep "Not After"

Şimdi dosya izinlerini ayarlayalım. Bu adımı atlarsan MySQL başlamayı reddedebilir:

chown -R mysql:mysql /etc/mysql/ssl/
chmod 750 /etc/mysql/ssl/
chmod 640 /etc/mysql/ssl/*.pem
chmod 600 /etc/mysql/ssl/server-key.pem
chmod 600 /etc/mysql/ssl/ca-key.pem
chmod 600 /etc/mysql/ssl/client-key.pem

MySQL Sunucu Yapılandırması

/etc/mysql/mysql.conf.d/mysqld.cnf veya /etc/my.cnf dosyasını düzenle (dağıtıma göre değişir):

[mysqld]
# SSL/TLS yapılandırması
ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem

# Minimum TLS versiyonu - TLS 1.2'nin altını kabul etme
tls_version=TLSv1.2,TLSv1.3

# Cipher suite kısıtlaması (isteğe bağlı ama önerilen)
ssl-cipher=ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256

# Sunucuyu yeniden başlat

Yapılandırmayı yaptıktan sonra servisi yeniden başlat ve SSL’in aktif olup olmadığını kontrol et:

systemctl restart mysql

# SSL durumunu kontrol et
mysql -u root -p -e "SHOW VARIABLES LIKE '%ssl%';"
mysql -u root -p -e "SHOW VARIABLES LIKE 'tls_version';"

# Bağlantı durumu
mysql -u root -p -e "SHOW STATUS LIKE 'Ssl_cipher';"

have_ssl değerinin YES göstermesi gerekiyor. DISABLED görüyorsan sertifika yollarını ve izinleri tekrar kontrol et.

SSL Zorunlu Kullanıcı Oluşturma

Sunucu SSL destekliyor ama bağlanan kullanıcıları şifreleme kullanmaya zorlayabilirsin. Bu en kritik adımlardan biri:

# SSL zorunlu kullanıcı oluştur
mysql -u root -p <<'EOF'
-- Uygulama kullanıcısı - SSL zorunlu
CREATE USER 'appuser'@'10.0.1.%' IDENTIFIED BY 'GucluSifre123!'
REQUIRE SSL;

-- Mutual TLS gerektiren kullanıcı - hem SSL hem istemci sertifikası zorunlu
CREATE USER 'appuser_mtls'@'10.0.1.%' IDENTIFIED BY 'GucluSifre456!'
REQUIRE X509;

-- Belirli bir sertifika CN'i ile gelen kullanıcı
CREATE USER 'appuser_strict'@'10.0.1.%' IDENTIFIED BY 'GucluSifre789!'
REQUIRE SUBJECT '/C=TR/ST=Istanbul/L=Istanbul/O=SirketAdi/CN=app-client'
AND ISSUER '/C=TR/ST=Istanbul/L=Istanbul/O=SirketAdi/CN=MySQL-CA';

-- Yetkilendirme
GRANT SELECT, INSERT, UPDATE, DELETE ON uygulama_db.* TO 'appuser'@'10.0.1.%';
GRANT SELECT, INSERT, UPDATE, DELETE ON uygulama_db.* TO 'appuser_mtls'@'10.0.1.%';
FLUSH PRIVILEGES;
EOF

Mevcut kullanıcıların SSL gereksinimini de değiştirebilirsin:

mysql -u root -p -e "ALTER USER 'mevcutuser'@'%' REQUIRE SSL;"

İstemci Bağlantısı Test Etme

Sunucu tarafını bitirdik, şimdi bağlantıyı test edelim. İstemci sertifikalarını uygulama sunucusuna taşıyalım:

# Uygulama sunucusuna sertifikaları kopyala
scp /etc/mysql/ssl/ca-cert.pem appserver:/etc/mysql-client/
scp /etc/mysql/ssl/client-cert.pem appserver:/etc/mysql-client/
scp /etc/mysql/ssl/client-key.pem appserver:/etc/mysql-client/

# Uygulama sunucusunda izinleri ayarla
ssh appserver "chmod 640 /etc/mysql-client/*.pem && 
  chown appuser:appuser /etc/mysql-client/*.pem"

Test bağlantısı:

# Sadece CA ile SSL bağlantısı (REQUIRE SSL kullanıcısı için)
mysql -h db.sirket.local 
  -u appuser 
  -p 
  --ssl-ca=/etc/mysql-client/ca-cert.pem 
  -e "SHOW STATUS LIKE 'Ssl_cipher';"

# Mutual TLS bağlantısı (REQUIRE X509 kullanıcısı için)
mysql -h db.sirket.local 
  -u appuser_mtls 
  -p 
  --ssl-ca=/etc/mysql-client/ca-cert.pem 
  --ssl-cert=/etc/mysql-client/client-cert.pem 
  --ssl-key=/etc/mysql-client/client-key.pem 
  -e "SELECT current_user(), @@ssl_cipher;"

# SSL olmadan bağlanmaya çalış - bu başarısız olmalı
mysql -h db.sirket.local 
  -u appuser_mtls 
  -p 
  --ssl-mode=DISABLED 
  -e "SELECT 1;"
# Beklenen hata: ERROR 1045 (28000): Access denied

Uygulama Tarafında SSL Entegrasyonu

Test bağlantısı geçti, şimdi uygulamanı da bu yapıya adapte etmen gerekiyor.

PHP (PDO) için:

# PHP ile SSL bağlantısı
cat > /var/www/html/db_config.php <<'EOF'
<?php
$dsn = 'mysql:host=db.sirket.local;dbname=uygulama_db;charset=utf8mb4';
$options = [
    PDO::MYSQL_ATTR_SSL_CA => '/etc/mysql-client/ca-cert.pem',
    PDO::MYSQL_ATTR_SSL_CERT => '/etc/mysql-client/client-cert.pem',
    PDO::MYSQL_ATTR_SSL_KEY => '/etc/mysql-client/client-key.pem',
    PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];

try {
    $pdo = new PDO($dsn, 'appuser_mtls', 'GucluSifre456!', $options);
    echo "SSL bağlantısı başarılın";
} catch (PDOException $e) {
    error_log("DB Bağlantı Hatası: " . $e->getMessage());
    die("Bağlantı kurulamadı");
}
EOF

Python (mysql-connector) için:

# Python ile SSL bağlantısı
cat > /opt/app/db_connect.py <<'EOF'
import mysql.connector
import ssl

ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_ctx.load_verify_locations('/etc/mysql-client/ca-cert.pem')
ssl_ctx.load_cert_chain(
    certfile='/etc/mysql-client/client-cert.pem',
    keyfile='/etc/mysql-client/client-key.pem'
)
ssl_ctx.verify_mode = ssl.CERT_REQUIRED

conn = mysql.connector.connect(
    host='db.sirket.local',
    user='appuser_mtls',
    password='GucluSifre456!',
    database='uygulama_db',
    ssl_context=ssl_ctx,
    ssl_verify_identity=True
)

cursor = conn.cursor()
cursor.execute("SHOW STATUS LIKE 'Ssl_cipher'")
print(cursor.fetchone())
conn.close()
EOF

Node.js için:

# Node.js ile SSL bağlantısı
cat > /opt/nodeapp/db.js <<'EOF'
const mysql = require('mysql2');
const fs = require('fs');

const connection = mysql.createConnection({
  host: 'db.sirket.local',
  user: 'appuser_mtls',
  password: 'GucluSifre456!',
  database: 'uygulama_db',
  ssl: {
    ca: fs.readFileSync('/etc/mysql-client/ca-cert.pem'),
    cert: fs.readFileSync('/etc/mysql-client/client-cert.pem'),
    key: fs.readFileSync('/etc/mysql-client/client-key.pem'),
    rejectUnauthorized: true
  }
});

connection.query('SHOW STATUS LIKE "Ssl_cipher"', (err, results) => {
  if (err) throw err;
  console.log('SSL Cipher:', results[0].Value);
  connection.end();
});
EOF

Sertifika Yenileme Otomasyonu

Sertifikalar 3650 gün (10 yıl) ayarladık ama bunu bir otomasyon rutinine bağlamak daha sağlıklı. Prod ortamda sertifikanın sessizce expire olması sonrasında arama yapılacak anılar bırakır.

#!/bin/bash
# /usr/local/bin/mysql-cert-check.sh

CERT_DIR="/etc/mysql/ssl"
WARN_DAYS=90
ALERT_EMAIL="[email protected]"

check_cert() {
    local cert_file="$1"
    local cert_name="$2"
    
    expiry=$(openssl x509 -enddate -noout -in "$cert_file" | cut -d= -f2)
    expiry_epoch=$(date -d "$expiry" +%s)
    current_epoch=$(date +%s)
    days_left=$(( (expiry_epoch - current_epoch) / 86400 ))
    
    if [ "$days_left" -lt "$WARN_DAYS" ]; then
        echo "UYARI: $cert_name sertifikasi $days_left gun sonra surecek!"
        echo "UYARI: $cert_name sertifikasi $days_left gun sonra surecek!" | 
            mail -s "[MySQL SSL] Sertifika Suresi Uyarisi" "$ALERT_EMAIL"
    else
        echo "OK: $cert_name - $days_left gun kaldi"
    fi
}

check_cert "$CERT_DIR/ca-cert.pem" "CA"
check_cert "$CERT_DIR/server-cert.pem" "Server"
check_cert "$CERT_DIR/client-cert.pem" "Client"

Bunu crontab’a ekle:

chmod +x /usr/local/bin/mysql-cert-check.sh
echo "0 9 * * 1 root /usr/local/bin/mysql-cert-check.sh" >> /etc/cron.d/mysql-ssl-check

Firewall ve Ağ Katmanı Güvenliği

SSL tek başına yeterli değil. Ağ katmanında da kısıtlamaları koyman gerekiyor:

# iptables ile sadece belirli IP'lere MySQL portunu aç
# Önce mevcut kuralları yedekle
iptables-save > /etc/iptables.backup

# Uygulama sunucusu IP bloğuna izin ver
iptables -A INPUT -p tcp --dport 3306 -s 10.0.1.0/24 -j ACCEPT

# Monitoring sunucusuna izin ver
iptables -A INPUT -p tcp --dport 3306 -s 10.0.2.10/32 -j ACCEPT

# Geri kalanı engelle
iptables -A INPUT -p tcp --dport 3306 -j DROP

# Kalıcı hale getir
iptables-save > /etc/iptables/rules.v4

# Firewalld kullananlar için
firewall-cmd --zone=internal --add-source=10.0.1.0/24
firewall-cmd --zone=internal --add-service=mysql
firewall-cmd --zone=public --remove-service=mysql --permanent
firewall-cmd --runtime-to-permanent

MariaDB İçin Farklar

MariaDB’de SSL yapılandırması büyük ölçüde aynı, ama birkaç farklılık var:

  • tls_version parametresi MariaDB 10.4.6 ve sonrasında destekleniyor
  • MariaDB mysql_ssl_rsa_setup aracını desteklemeyebilir, openssl ile elle üretmek daha güvenli
  • REQUIRE CIPHER seçeneği MariaDB’de de çalışıyor
  • MariaDB’de ssl-mode yerine --ssl ve --ssl-verify-server-cert kullanılıyor
# MariaDB için yapılandırma kontrolü
mysql -u root -p -e "SELECT @@have_ssl, @@ssl_ca, @@ssl_cert;"

Yaygın Hatalar ve Çözümleri

Hata: SSL connection error: error:00000001:lib(0):func(0):reason(1)

Bu genellikle sertifika CN’inin bağlandığın hostname ile eşleşmediğinde çıkar. Server sertifikasını oluştururken CN olarak ne yazdıysan, o hostname veya IP ile bağlanman gerekiyor.

Hata: ERROR 2026 (HY000): SSL connection error: error:0407008A

CA sertifikasının dosya izinlerini kontrol et. mysql kullanıcısı o dosyayı okuyabilmeli.

SSL aktif ama Ssl_cipher boş geliyor:

# Hangi bağlantının SSL kullandığını kontrol et
mysql -u root -p -e "SELECT user, host, ssl_type FROM mysql.user WHERE user='appuser';"

# Aktif bağlantılarda SSL durumu
mysql -u root -p -e "SELECT * FROM information_schema.processlist WHERE id = CONNECTION_ID();"

REQUIRE SSL ayarına rağmen SSL’siz bağlanılabiliyorsa:

REQUIRE SSL kullanıcı seviyesinde SSL gerektirir. Ek olarak sunucu genelinde require_secure_transport=ON ayarını yaparak tüm bağlantıları SSL’e zorlayabilirsin. Bu ayar my.cnf içine require_secure_transport=ON olarak eklenir.

SSL Bağlantılarını İzleme

SSL trafiğini log’lamak ve anomali tespiti için şunu uygulayabilirsin:

# SSL bağlantı istatistikleri
mysql -u root -p -e "
SELECT 
  VARIABLE_NAME, 
  VARIABLE_VALUE 
FROM performance_schema.global_status 
WHERE VARIABLE_NAME IN (
  'Ssl_accepts',
  'Ssl_finished_accepts',
  'Ssl_connects',
  'Ssl_finished_connects',
  'Ssl_cipher'
);"

# Hangi kullanıcıların SSL'siz bağlandığını bul
mysql -u root -p -e "
SELECT 
  user, 
  host, 
  db, 
  command,
  ssl_cipher
FROM information_schema.processlist pl
LEFT JOIN performance_schema.threads t ON pl.id = t.processlist_id
WHERE ssl_cipher IS NULL OR ssl_cipher = '';"

Sonuç

MySQL SSL yapılandırması ilk bakışta karmaşık görünse de adımları tek tek uyguladığında yerli yerine oturuyor. Özetlersek:

  • CA, sunucu ve istemci sertifikalarını openssl ile üret
  • Dosya izinlerini doğru ayarla, mysql kullanıcısı anahtar dosyalarını okumalı
  • my.cnf içinde SSL yollarını ve minimum TLS versiyonunu belirt
  • Kullanıcıları REQUIRE SSL veya REQUIRE X509 ile oluştur
  • Ağ katmanında firewall ile 3306 portunu sadece yetkili IP’lere aç
  • Sertifika expiry izlemesini otomasyona bağla

Mutual TLS, yani REQUIRE X509 yaklaşımı en güçlü seçenek. Sadece şifreyi değil, sertifikayı da çalmak gerekiyor bağlanmak için. Bir e-ticaret projemizde bu yapıya geçtikten sonra PCI-DSS denetiminde veritabanı bağlantısı kaleminden sorunsuz geçtik. Doğru yapılandırıldığında hem güvenlik sağlıyor hem de uyumluluk gereksinimlerini karşılıyor.

Yorum yapın