Production ortamında OpenVPN kullanıyorsanız ve tek bir sunucuya bağlı kalıyorsanız, o sunucu düştüğünde tüm VPN bağlantılarınız da düşer. Bu durum, uzaktan çalışan ekipler veya kritik altyapıya VPN üzerinden erişen sistemler için ciddi bir sorun oluşturur. Yüksek erişilebilirlik (High Availability – HA) mimarisi kurarak bu tek nokta arızasını ortadan kaldırabilirsiniz. Bu yazıda, iki OpenVPN sunucusunu aktif-pasif modda çalıştıran, Keepalived ile sanal IP yöneten ve otomatik failover yapabilen bir sistem kuracağız.
Mimariye Genel Bakış
Kuracağımız sistemde iki OpenVPN sunucusu olacak:
- Primary (vpn1): 192.168.1.10 – Aktif olarak çalışan ana sunucu
- Secondary (vpn2): 192.168.1.11 – Pasif modda bekleyen yedek sunucu
- Virtual IP (VIP): 192.168.1.20 – İstemcilerin bağlandığı sanal IP
İstemciler her zaman sanal IP adresine bağlanır. Primary sunucu çöktüğünde Keepalived devreye girer ve sanal IP’yi secondary sunucuya taşır. Bu geçiş genellikle 2-5 saniye içinde tamamlanır. OpenVPN istemcileri bağlantı kesildiğinde otomatik olarak yeniden bağlanır ve ikinci sunucuyu aktif olarak bulur.
Gerçek dünya senaryosu olarak düşünün: 50 kişilik bir şirkette tüm çalışanlar VPN üzerinden iç sistemlere erişiyor. Saat 14:00’te primary sunucunun diski doluyor ve sistem yanıt vermez hale geliyor. HA yapısı olmadan IT ekibi sorunu fark edene kadar 20-30 dakika boyunca kimse çalışamaz. HA yapısıyla bu kesinti maksimum birkaç saniyeye iner.
Ön Gereksinimler ve Hazırlık
Her iki sunucuda da aşağıdaki paketlerin kurulu olması gerekiyor:
# Ubuntu/Debian için
apt update && apt install -y openvpn easy-rsa keepalived rsync
# CentOS/RHEL için
yum install -y epel-release
yum install -y openvpn easy-rsa keepalived rsync
Firewall ayarlarını her iki sunucuda da yapılandırın:
# UFW kullanıyorsanız (Ubuntu)
ufw allow 1194/udp
ufw allow 1194/tcp
# Keepalived için VRRP protokolü
ufw allow proto ah from 192.168.1.0/24
ufw allow proto vrrp from 192.168.1.0/24
# Firewalld kullanıyorsanız (CentOS)
firewall-cmd --permanent --add-service=openvpn
firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
firewall-cmd --reload
IP yönlendirmeyi aktif edin, bu her iki sunucuda da yapılmalı:
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv4.ip_nonlocal_bind = 1" >> /etc/sysctl.conf
sysctl -p
ip_nonlocal_bind parametresi çok önemli. Bu olmadan OpenVPN ve Keepalived, henüz kendilerine atanmamış sanal IP adresine bind olamaz.
PKI Altyapısı Kurulumu
Sertifika altyapısını sadece primary sunucuda oluşturuyoruz, sonra secondary’ye kopyalayacağız:
# Primary sunucuda (vpn1)
mkdir -p /etc/openvpn/easy-rsa
cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
cd /etc/openvpn/easy-rsa
# vars dosyasını düzenle
cat > vars << 'EOF'
set_var EASYRSA_REQ_COUNTRY "TR"
set_var EASYRSA_REQ_PROVINCE "Istanbul"
set_var EASYRSA_REQ_CITY "Istanbul"
set_var EASYRSA_REQ_ORG "Sirket VPN"
set_var EASYRSA_REQ_EMAIL "[email protected]"
set_var EASYRSA_REQ_OU "IT"
set_var EASYRSA_ALGO "ec"
set_var EASYRSA_DIGEST "sha512"
set_var EASYRSA_KEY_SIZE 4096
EOF
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa gen-dh
./easyrsa build-server-full server nopass
./easyrsa build-client-full client1 nopass
# TLS Auth key oluştur
openvpn --genkey --secret /etc/openvpn/ta.key
Oluşturulan dosyaları secondary sunucuya kopyalayın. Bunların senkronize olması kritik:
# Primary'den secondary'ye sertifikaları kopyala
rsync -avz /etc/openvpn/easy-rsa/pki/ [email protected]:/etc/openvpn/easy-rsa/pki/
rsync -avz /etc/openvpn/ta.key [email protected]:/etc/openvpn/ta.key
OpenVPN Sunucu Yapılandırması
Her iki sunucuda da aynı yapılandırma dosyasını kullanacağız. Bu tutarlılık, failover sırasında istemcilerin sorunsuz bağlanması için önemli:
# Her iki sunucuda da /etc/openvpn/server.conf oluştur
cat > /etc/openvpn/server.conf << 'EOF'
# Temel ayarlar
port 1194
proto udp
dev tun
topology subnet
# Sertifikalar
ca /etc/openvpn/easy-rsa/pki/ca.crt
cert /etc/openvpn/easy-rsa/pki/issued/server.crt
key /etc/openvpn/easy-rsa/pki/private/server.key
dh /etc/openvpn/easy-rsa/pki/dh.pem
tls-auth /etc/openvpn/ta.key 0
# VPN ağı
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
# Yönlendirme
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
# Performans ve güvenilirlik
keepalive 5 30
cipher AES-256-GCM
auth SHA512
tls-version-min 1.2
compress lz4-v2
push "compress lz4-v2"
max-clients 100
# Loglama
status /var/log/openvpn/openvpn-status.log 30
log-append /var/log/openvpn/openvpn.log
verb 3
# HA için önemli: istemci IP atamalarını kaydet
client-config-dir /etc/openvpn/ccd
EOF
mkdir -p /var/log/openvpn /etc/openvpn/ccd
Burada keepalive 5 30 ayarına dikkat edin. Bu, her 5 saniyede bir ping göndermesi ve 30 saniye yanıt gelmezse bağlantıyı kesmesi anlamına gelir. HA ortamında bu değerleri düşük tutmak, istemcilerin failover sonrası daha hızlı yeniden bağlanmasını sağlar.
Keepalived Yapılandırması
Keepalived, VRRP protokolünü kullanarak iki sunucu arasında sanal IP’yi yönetir. Primary sunucuda master, secondary sunucuda backup olarak yapılandırıyoruz:
# PRIMARY sunucuda (/etc/keepalived/keepalived.conf)
cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
router_id VPN_PRIMARY
script_user root
enable_script_security
}
vrrp_script check_openvpn {
script "/usr/local/bin/check_openvpn.sh"
interval 3
weight -20
fall 2
rise 2
}
vrrp_instance VPN_HA {
state MASTER
interface eth0
virtual_router_id 51
priority 110
advert_int 1
authentication {
auth_type PASS
auth_pass VpnH4S3cur3!
}
virtual_ipaddress {
192.168.1.20/24
}
track_script {
check_openvpn
}
notify_master "/usr/local/bin/vpn_master.sh"
notify_backup "/usr/local/bin/vpn_backup.sh"
}
EOF
# SECONDARY sunucuda (/etc/keepalived/keepalived.conf)
cat > /etc/keepalived/keepalived.conf << 'EOF'
global_defs {
router_id VPN_SECONDARY
script_user root
enable_script_security
}
vrrp_script check_openvpn {
script "/usr/local/bin/check_openvpn.sh"
interval 3
weight -20
fall 2
rise 2
}
vrrp_instance VPN_HA {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass VpnH4S3cur3!
}
virtual_ipaddress {
192.168.1.20/24
}
track_script {
check_openvpn
}
notify_master "/usr/local/bin/vpn_master.sh"
notify_backup "/usr/local/bin/vpn_backup.sh"
}
EOF
Priority değerleri önemli. Primary 110, secondary 100. OpenVPN servisi primary’de çöktüğünde weight -20 devreye girer ve primary’nin skoru 90’a düşer, secondary 100 ile kazanır ve VIP’i devralır.
Sağlık Kontrol Scripti
Her iki sunucuda da aynı sağlık kontrol scriptini oluşturun:
cat > /usr/local/bin/check_openvpn.sh << 'EOF'
#!/bin/bash
# OpenVPN process kontrolü
if ! pgrep -x "openvpn" > /dev/null; then
echo "OpenVPN process bulunamadı, yeniden başlatılıyor..."
systemctl start openvpn@server
sleep 3
if ! pgrep -x "openvpn" > /dev/null; then
echo "OpenVPN başlatılamadı!"
exit 1
fi
fi
# TUN interface kontrolü
if ! ip link show tun0 > /dev/null 2>&1; then
echo "tun0 interface bulunamadı!"
exit 1
fi
# Port dinleme kontrolü
if ! ss -ulnp | grep -q ":1194 "; then
echo "OpenVPN port 1194'te dinlemiyor!"
exit 1
fi
exit 0
EOF
chmod +x /usr/local/bin/check_openvpn.sh
Notify Scriptleri
Bu scriptler, bir sunucu master veya backup rolünü aldığında tetiklenir:
cat > /usr/local/bin/vpn_master.sh << 'EOF'
#!/bin/bash
# Master rolü alındığında çalışır
LOGFILE="/var/log/keepalived-notify.log"
echo "$(date): Bu sunucu MASTER rolünü aldı" >> $LOGFILE
# OpenVPN servisinin çalışır durumda olduğundan emin ol
systemctl start openvpn@server
# Opsiyonel: Slack/email bildirimi
# curl -s -X POST https://hooks.slack.com/services/XXX -d '{"text":"VPN Primary aktif"}'
EOF
cat > /usr/local/bin/vpn_backup.sh << 'EOF'
#!/bin/bash
# Backup rolüne geçildiğinde çalışır
LOGFILE="/var/log/keepalived-notify.log"
echo "$(date): Bu sunucu BACKUP rolüne geçti" >> $LOGFILE
EOF
chmod +x /usr/local/bin/vpn_master.sh
chmod +x /usr/local/bin/vpn_backup.sh
İstemci IP Senkronizasyonu
HA mimarisinin en can sıkıcı kısmı şu: Bir istemci primary’ye bağlandığında 10.8.0.2 IP’sini alıyor, failover olduğunda secondary’ye bağlandığında farklı bir IP alabilir. Bu, uygulamalar IP bazlı erişim kontrolü yapıyorsa sorun çıkarır. Bunu çözmek için ipp.txt dosyasını iki sunucu arasında senkronize ediyoruz:
# Her iki sunucuda passwordless SSH ayarla
ssh-keygen -t ed25519 -f /root/.ssh/vpn_sync -N ""
# Primary'nin public key'ini secondary'ye ekle
# Secondary'nin public key'ini primary'ye ekle
# (Bu adımı manuel yapmanız gerekiyor)
# Primary sunucuda senkronizasyon scripti
cat > /usr/local/bin/sync_vpn_state.sh << 'EOF'
#!/bin/bash
PRIMARY="192.168.1.10"
SECONDARY="192.168.1.11"
SYNC_KEY="/root/.ssh/vpn_sync"
# Hangi sunucuda çalışıyoruz?
MY_IP=$(hostname -I | awk '{print $1}')
if [ "$MY_IP" = "$PRIMARY" ]; then
REMOTE=$SECONDARY
else
REMOTE=$PRIMARY
fi
# IPP dosyasını senkronize et
rsync -az -e "ssh -i $SYNC_KEY -o StrictHostKeyChecking=no"
/var/log/openvpn/ipp.txt
root@${REMOTE}:/var/log/openvpn/ipp.txt
# CCD dizinini senkronize et
rsync -az -e "ssh -i $SYNC_KEY -o StrictHostKeyChecking=no"
/etc/openvpn/ccd/
root@${REMOTE}:/etc/openvpn/ccd/
EOF
chmod +x /usr/local/bin/sync_vpn_state.sh
# Cron ile her dakika çalıştır
echo "* * * * * root /usr/local/bin/sync_vpn_state.sh >> /var/log/vpn_sync.log 2>&1"
>> /etc/crontab
Servislerin Başlatılması ve Doğrulama
Her şeyi ayarladıktan sonra servisleri sırayla başlatın:
# Her iki sunucuda
systemctl enable openvpn@server
systemctl start openvpn@server
systemctl enable keepalived
systemctl start keepalived
# Primary'de VIP'in atandığını kontrol et
ip addr show eth0 | grep 192.168.1.20
# Keepalived durumunu kontrol et
systemctl status keepalived
journalctl -u keepalived -f
Failover testini manuel olarak yapın. Primary sunucuda OpenVPN’i durdurun ve VIP’in secondary’ye geçip geçmediğini gözlemleyin:
# PRIMARY sunucuda
systemctl stop openvpn@server
# Birkaç saniye bekle, sonra secondary'de kontrol et
# SECONDARY sunucuda
ip addr show eth0 | grep 192.168.1.20
# Çıktıda 192.168.1.20 görünmeli
# Primary'yi tekrar başlat
systemctl start openvpn@server
# VIP tekrar primary'ye dönmeli (preempt davranışı)
İstemci Yapılandırması
İstemci tarafında da bazı ayarlamalar yapmanız gerekiyor. En önemli kısım, istemcinin bağlantı koptuğunda otomatik olarak yeniden bağlanması:
# İstemci yapılandırma dosyası (client.ovpn)
cat > client.ovpn << 'EOF'
client
dev tun
proto udp
# Sanal IP'ye bağlan, sunucu IP'sine değil
remote 192.168.1.20 1194
# Bağlantı koptuğunda sonsuz deneme
resolv-retry infinite
nobind
persist-key
persist-tun
# Sertifikalar (inline olarak eklenebilir)
ca ca.crt
cert client1.crt
key client1.key
tls-auth ta.key 1
# Güvenlik
cipher AES-256-GCM
auth SHA512
tls-version-min 1.2
verify-x509-name server name
# Bağlantı yeniden deneme ayarları
connect-retry 5 10
connect-retry-max unlimited
# Kompresyon (sunucuyla uyumlu)
compress lz4-v2
verb 3
EOF
connect-retry 5 10 parametresi ilk 5 saniye sonra dener, sonra 10 saniyede bir dener. connect-retry-max unlimited sayesinde sonsuza kadar denemeye devam eder. Bu, failover sonrası istemcinin otomatik olarak yeni aktif sunucuya bağlanmasını sağlar.
İzleme ve Alerting
Üretim ortamında HA sisteminin düzgün çalışıp çalışmadığını izlemek kritik. Basit bir monitoring scripti:
cat > /usr/local/bin/vpn_ha_monitor.sh << 'EOF'
#!/bin/bash
# Her iki sunucunun durumunu kontrol eden script
# Zabbix/Nagios uyumlu çıktı verir
PRIMARY="192.168.1.10"
SECONDARY="192.168.1.11"
VIP="192.168.1.20"
PORT=1194
EXIT_CODE=0
# VIP erişilebilir mi?
if nc -z -u -w3 $VIP $PORT 2>/dev/null; then
echo "OK: VPN VIP ($VIP:$PORT) erişilebilir"
else
echo "CRITICAL: VPN VIP ($VIP:$PORT) erişilemiyor!"
EXIT_CODE=2
fi
# Primary durumu
PRIMARY_STATUS=$(ssh -i /root/.ssh/vpn_sync -o ConnectTimeout=5
root@$PRIMARY "systemctl is-active openvpn@server" 2>/dev/null)
echo "Primary ($PRIMARY) OpenVPN: $PRIMARY_STATUS"
# Secondary durumu
SECONDARY_STATUS=$(ssh -i /root/.ssh/vpn_sync -o ConnectTimeout=5
root@$SECONDARY "systemctl is-active openvpn@server" 2>/dev/null)
echo "Secondary ($SECONDARY) OpenVPN: $SECONDARY_STATUS"
# Her iki sunucu da down ise kritik
if [ "$PRIMARY_STATUS" != "active" ] && [ "$SECONDARY_STATUS" != "active" ]; then
echo "CRITICAL: Her iki VPN sunucusu da down!"
EXIT_CODE=2
elif [ "$PRIMARY_STATUS" != "active" ] || [ "$SECONDARY_STATUS" != "active" ]; then
echo "WARNING: Bir VPN sunucusu down, HA aktif ama dikkat gerekli!"
EXIT_CODE=1
fi
exit $EXIT_CODE
EOF
chmod +x /usr/local/bin/vpn_ha_monitor.sh
Yaygın Sorunlar ve Çözümleri
Split brain durumu: Her iki sunucu da kendini master sanabilir. Bu genellikle ağ segmentasyonundan kaynaklanır. VRRP trafiğinin iki sunucu arasında kesintisiz akmasını sağlayın. Dedicated bir management interface kullanmak bu sorunu büyük ölçüde çözer.
VIP’in geri dönmemesi: nopreempt seçeneğini keepalived.conf’a eklerseniz, primary tekrar ayağa kalksa bile VIP secondary’de kalır. Bu bazı senaryolarda istenen davranıştır, ancak varsayılan olarak preempt aktiftir.
Sertifika uyumsuzluğu: İstemcilerin secondary’ye bağlanırken sertifika hatası alması, sertifikaların düzgün kopyalanmadığını gösterir. rsync komutunu tekrar çalıştırın ve dosya permission’larının 600 olduğunu kontrol edin.
Keepalived auth mismatch: Her iki sunucudaki auth_pass değerinin birebir aynı olması şart. Boşluk karakterine bile dikkat edin.
Sonuç
Bu yapıyı kurduğunuzda elinizde gerçek anlamda production-ready bir VPN altyapısı olacak. Keepalived’in VRRP mekanizması, sağlık kontrol scriptleri ve istemci tarafındaki otomatik yeniden bağlanma özellikleri bir arada çalışarak birkaç saniyelik kesinti süreleri elde etmenizi sağlıyor.
Uygulamada karşılaşacağınız en büyük zorluk muhtemelen state senkronizasyonu olacak. İstemci IP atamalarının iki sunucu arasında tutarlı kalması için cron tabanlı rsync çözümü çoğu durumda yeterli, ancak daha kritik ortamlar için GlusterFS veya NFS gibi paylaşımlı depolama çözümlerine bakabilirsiniz.
Son olarak, bu sistemi kurduktan sonra düzenli olarak failover testleri yapın. “Çalışıyor” gibi görünen bir HA sistemi, gerçek bir arıza anında beklenmedik davranışlar gösterebilir. Ayda en az bir kez planlı failover testi yapmak iyi bir pratik olarak yerleşmeli. Hem sistemin gerçekten çalıştığını doğrularsınız, hem de ekibiniz prosedürlere alışık olur.