筆者はIPSec/IKEv2にて自宅へVPN接続出来る環境を作っていましたが、VPNサーバが1台しかないのは心もとないため 負荷分散や冗長化を目的としてLVSを用いたL4DSRを構築し、VPNサーバを複数台の構成にしました。その備忘録としてここにまとめます。 また今回はVPNサーバを構築してますが、DNSやHTTPサーバももちろんOKです。
VPN自体は以下のDockerコンテナを使って構築。構築手順は省略します。 https://github.com/hwdsl2/docker-ipsec-vpn-server
docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
ipsec-vpn-server ikev2-vpn-vpn "/opt/src/run.sh" vpn 2 hours ago Up 2 hours 0.0.0.0:500->500/udp, :::500->500/udp, 0.0.0.0:4500->4500/udp, :::4500->4500/udp
リアルサーバに来たパケットを、LoadBalancerを経由せずに直接クライアントに返却する方式です。 イメージとしては以下の通り。
必要なパッケージをインストール
sudo apt install -y keepalived ipvsadm
参考にされる場合は、インターフェイス名などにご注意を。
パケット転送を有効にする。
net.ipv4.ip_forward = 1
以下で反映。
sudo sysctl -p
keepalivedで実際に仮想IPを割り当てます。
! Configuration File for keepalived
global_defs {
router_id LVS_IKE
}
vrrp_instance VRRP_1 {
! priorityでMasterの判定を行うので2台ともにBACKUP
state BACKUP
! VRRPのインターフェースを指定
interface ens18
! VRRPのID
virtual_router_id 52
! Master判定優先度(Master側の値を大きくする)
priority 100
! VRRP送信間隔
advert_int 3
! フェイルバックはしない
nopreempt
virtual_ipaddress {
192.168.2.10/22 dev ens18
}
}
include virtualservers/*.conf
ヘルスチェックの設定
! Configuration File for virtualserver
virtual_server 192.168.2.10 500 {
! 監視周期 5秒
delay_loop 5
! ラウンドロビンで負荷分散する
lvs_sched rr
! パケット転送方式は、Direct Return。
lvs_method DR
! プロトコル指定
protocol UDP
! sv-a-vpn
real_server 192.168.1.30 500 {
! 重み
weight 1
! ヘルスチェック失敗の場合は削除ではなくWeightをゼロにする
inhibit_on_failure
VPN_CHECK {
connect_port 500
! ポーリングのタイムアウト時間(秒)
connect_timeout 5
! リトライ回数
retry 3
}
}
! sv-b-vpn
real_server 192.168.1.31 500 {
! 重み
weight 1
! ヘルスチェック失敗の場合は削除ではなくWeightをゼロにする
inhibit_on_failure
VPN_CHECK {
connect_port 500
! ポーリングのタイムアウト時間(秒)
connect_timeout 5
! リトライ回数
retry 3
}
}
}
! Configuration File for virtualserver
virtual_server 192.168.2.10 4500 {
! 監視周期 5秒
delay_loop 5
! ラウンドロビンで負荷分散する
lvs_sched rr
! パケット転送方式は、Direct Return。
lvs_method DR
! プロトコル指定
protocol UDP
! sv-a-vpn
real_server 192.168.1.30 4500 {
! 重み
weight 1
! ヘルスチェック失敗の場合は削除ではなくWeightをゼロにする
inhibit_on_failure
VPN_CHECK {
connect_port 4500
! ポーリングのタイムアウト時間(秒)
connect_timeout 5
! リトライ回数
retry 3
}
}
! sv-b-vpn
real_server 192.168.1.31 4500 {
! 重み
weight 1
! ヘルスチェック失敗の場合は削除ではなくWeightをゼロにする
inhibit_on_failure
VPN_CHECK {
connect_port 4500
! ポーリングのタイムアウト時間(秒)
connect_timeout 5
! リトライ回数
retry 3
}
}
}
L4での負荷分散を実施します。
# udp 500
sudo ipvsadm -A -u 192.168.2.10:500 -s wlc
sudo ipvsadm -a -u 192.168.2.10:500 -r 192.168.1.30:500 -g -w 1
sudo ipvsadm -a -u 192.168.2.10:500 -r 192.168.1.31:500 -g -w 1
# udp 4500
sudo ipvsadm -A -u 192.168.2.10:4500 -s wlc
sudo ipvsadm -a -u 192.168.2.10:4500 -r 192.168.1.30:4500 -g -w 1
sudo ipvsadm -a -u 192.168.2.10:4500 -r 192.168.1.31:4500 -g -w 1
# 設定の確認
sudo ipvsadm -S
# 動作状況の確認
sudo ipvsadm -l
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
UDP 192.168.2.10:isakmp wlc
-> 192.168.1.30:isakmp Route 1 0 0
-> 192.168.1.31:isakmp Route 1 0 0
UDP 192.168.2.10:ipsec-nat-t wlc
-> 192.168.1.30:ipsec-nat-t Route 1 0 0
-> 192.168.1.31:ipsec-nat-t Route 1 0 0
ロードバランサの設定は以上です。
リアルサーバではループバックアドレスの変更を行います。
# This is the network config written by 'subiquity'
network:
ethernets:
ens18:
dhcp4: false
addresses: [192.168.1.30/22]
gateway4: 192.168.0.1
nameservers:
addresses: [192.168.0.1, 1.1.1.1]
# 以下を追加
lo:
renderer: networkd
match:
name: lo
addresses:
- 192.168.2.10/32
以下で反映。
sudo netplan apply
確認。
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet 192.168.2.10/32 scope global lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 36:cc:d5:2e:70:7a brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 192.168.1.30/22 brd 192.168.3.255 scope global ens18
valid_lft forever preferred_lft forever
inet6 fe80::34cc:d5ff:fe2e:707a/64 scope link
valid_lft forever preferred_lft forever
...
また、ARP応答をしないようにするため次のカーネルパラメータを追加。
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
以下で反映。
sudo sysctl -p
以上で構築は完了です。 この状態で外部から接続出来るように、DNSやNAPTの設定を済ませた上でiPhoneから接続すると このように無事に接続できました。
またLoadBalancerの片方を落とした場合に、IPが切り替わるかを下記のコマンドで確認すると良いでしょう。
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 2a:2d:3d:f0:e0:e8 brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 192.168.1.21/22 brd 192.168.3.255 scope global ens18
valid_lft forever preferred_lft forever
inet 192.168.2.10/22 scope global secondary ens18
valid_lft forever preferred_lft forever
inet6 fe80::282d:3dff:fef0:e0e8/64 scope link
valid_lft forever preferred_lft forever