Akamai's cloud computing servicesの複数リージョンをサイト間VPNで接続する

# Akamai's cloud computing servicesのグローバルネットワークについて
Akamai's cloud computing servicesの各リージョン間にはAkamai Global Networkが構築されており、ほぼすべてのリージョンがこの経路を利用して通信することができます。

https://www.linode.com/blog/networking/behind-the-scenes-backed-by-a-bigger-network/

実際に通信する際には、各リージョンで作成したVLAN内のインスタンスから別リージョンに接続する際にPublicIP経由でアクセスする必要があります。
しかし、通信を暗号化させた状態で通信させたい要件がある際には経路を安全に確保する必要があります。
今回はリージョン間の通信をVPNで接続し、冗長性を保てる構成で接続する構成を紹介します。

本構成図は以下となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/5c0ea783-89b3-3cf5-ac6e-c715b8664cd8.png)

# 本構成で利用するオープンソースのソフトウェアについて

本構成を実現するために、いくつかのオープンソースのソフトウェアを利用しています。

- 安全な通信経路を確立するために、strongSwanを利用。
- 経路を冗長化するために、FRRoutingを利用。
- ゲートウェイを冗長化するために、Keepalivedを利用。

## strongSwan とは

strongSwanはLinuxカーネル上で動作するIPsecを実装できるVPNソリューションです。IKEv1とIKEv2をサポートしており、エンドツーエンドのセキュアな通信を提供することができます。

https://www.strongswan.org/

## FRRouting とは

Free Range Routing(FRR)は、Quaggaから派生したUNIX/Linuxプラットフォーム上で動作するルーティングプロトコルを管理するソリューションです。
BGP/OSPF/RIP/MPLSなどのさまざまなルーティングプロトコルを実装しており、Cisco独自のEIGRPもサポートしています。

https://frrouting.org/

## Keepalivedとは

KeepalivedはLinux上で動作するオープンソースソフトウェアで、サービスの高可用性とロードバランシングを提供します。Linux Virtual Serverを用いたレイヤー4ロードバランシングと、VRRPを利用した冗長化を実現し、サービスのダウンタイムを最小に抑えることができます。

https://www.keepalived.org/index.html

# Akamai's cloud computing servicesの2つのリージョンを接続する

今回の手順に関しては、シンガポールとUS アトランタのリージョンを接続する構成です。

## インスタンスの構築

今回、疎通確認用のインスタンスとVPN/ルーティング用のインスタンスは、Linode 4 GBで作成します。

OSはUbuntu 22.04 LTSを利用しています。

各インスタンスを起動したらPublic IP Addressを控えておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/65709238-4f33-1722-360a-db0ae71ed3ae.png)

また、同一リージョン内にあるインスタンスからの疎通を実現するため、VLANインターフェイスを追加します。

https://qiita.com/110hideki/items/aead6f2bc9ff1563641c

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/3a1700f5-2b57-e2cb-1b42-a1777fe62492.png)


## strongSwanのインストール

4台あるVPNインスタンスにstrongSwanをインストールします。

```
apt update
apt install strongswan
```

ipsec.confを設定し、トンネルインターフェースの設定をします。

```bash
root@localhost:~# vim /etc/ipsec.conf

config setup
        charondebug="all"
        uniqueids=yes
        strictcrlpolicy=no

conn TUNNEL1
        auto=start
        compress=no
        type=tunnel
        leftsubnet=0.0.0.0/0,::/0
        rightsubnet=0.0.0.0/0,::/0
        authby=secret
        keyexchange=ikev2
        fragmentation=yes
        ike=aes256-sha1-modp1024!
        esp=aes256-sha1!
        ikelifetime=28800s
        lifetime=3600s
        dpdaction=restart
        dpddelay=60s
        dpdtimeout=120s
        closeaction=none
        rekey=yes
        installpolicy=yes
        leftupdown=/etc/ipsec-vti.sh
        left=<自身のLinode Public IP>
        right=<対向のLinode Public IP>
        mark=101
```

認証にPSK: Pre-Shared Keyを利用するため、ランダムな文字列を発行します。

```
root@localhost:~# openssl rand -base64 32
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

発行したランダムな文字列をipsec.secretsに保存します。

```
root@localhost:~# vim /etc/ipsec.secrets
<自身のLinode Public IP> <対向のLinode Public IP> : PSK "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
```

トンネルインターフェースUP/DOWNの際に動作するスクリプトを記載します。

```bash
root@localhost:~# vim /etc/ipsec-vti.sh

#!/bin/bash

#
# /etc/ipsec-vti.sh
#

IP=$(which ip)
IPTABLES=$(which iptables)

PLUTO_MARK_OUT_ARR=(${PLUTO_MARK_OUT//// })
PLUTO_MARK_IN_ARR=(${PLUTO_MARK_IN//// })

case "$PLUTO_CONNECTION" in
    TUNNEL1)
        VTI_INTERFACE=vti1
        VTI_LOCALADDR=169.254.0.2/30 # 各インスタンスの自身のIPを入れてください
        VTI_REMOTEADDR=169.254.0.1/30 # 各インスタンスの対向のIPを入れてください
    ;;
esac

# output parameters to /var/log/syslog for debug
logger "ipsec-vti.sh: ================================================="
logger "ipsec-vti.sh: PLUTO_CONNECTION = ${PLUTO_CONNECTION}"
logger "ipsec-vti.sh: PLUTO_VERB = ${PLUTO_VERB}"
logger "ipsec-vti.sh: VTI_INTERFACE = ${VTI_INTERFACE}"
logger "ipsec-vti.sh: PLUTO_ME = ${PLUTO_ME}"
logger "ipsec-vti.sh: PLUTO_PEER = ${PLUTO_PEER}"
logger "ipsec-vti.sh: PLUTO_MARK_IN_ARR[0] = ${PLUTO_MARK_IN_ARR[0]}"
logger "ipsec-vti.sh: PLUTO_MARK_OUT_ARR[0] = ${PLUTO_MARK_OUT_ARR[0]}"
logger "ipsec-vti.sh: PLUTO_MARK_IN = ${PLUTO_MARK_IN}"
logger "ipsec-vti.sh: VTI_LOCALADDR = ${VTI_LOCALADDR}"
logger "ipsec-vti.sh: VTI_REMOTEADDR = ${VTI_REMOTEADDR}"
logger "ipsec-vti.sh: ================================================="

case "${PLUTO_VERB}" in
    up-client)
        $IP link add ${VTI_INTERFACE} type vti local ${PLUTO_ME} remote ${PLUTO_PEER} okey ${PLUTO_MARK_OUT_ARR[0]} ikey ${PLUTO_MARK_IN_ARR[0]}
        sysctl -w net.ipv4.conf.${VTI_INTERFACE}.disable_policy=1
        sysctl -w net.ipv4.conf.${VTI_INTERFACE}.rp_filter=2 || sysctl -w net.ipv4.conf.${VTI_INTERFACE}.rp_filter=0
        $IP addr add ${VTI_LOCALADDR} remote ${VTI_REMOTEADDR} dev ${VTI_INTERFACE}
        $IP link set ${VTI_INTERFACE} up mtu 1436
        $IPTABLES -t mangle -I FORWARD -o ${VTI_INTERFACE} -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
        $IPTABLES -t mangle -I INPUT -p esp -s ${PLUTO_PEER} -d ${PLUTO_ME} -j MARK --set-xmark ${PLUTO_MARK_IN}
        $IP route flush table 220
    ;;
    down-client)
        $IP link del ${VTI_INTERFACE}
        $IPTABLES -t mangle -D FORWARD -o ${VTI_INTERFACE} -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
        $IPTABLES -t mangle -D INPUT -p esp -s ${PLUTO_PEER} -d ${PLUTO_ME} -j MARK --set-xmark ${PLUTO_MARK_IN}
    ;;
esac

# Enable IPv4 forwarding
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.eth0.disable_xfrm=1
sysctl -w net.ipv4.conf.eth0.disable_policy=1
sysctl -w net.ipv4.conf.eth1.disable_xfrm=1
sysctl -w net.ipv4.conf.eth1.disable_policy=1
```

実行権限を付与します。

```
chmod +x /etc/ipsec-vti.sh
```

サービスを起動します。

```
systemctl start strongswan-starter.service
systemctl enable strongswan-starter.service
```

トンネルがUPになっており、接続先へICMPの疎通が取れることを確認します。

```
root@localhost:~# ipsec status
Security Associations (1 up, 0 connecting):
     TUNNEL1[35]: ESTABLISHED 110 minutes ago, 139.162.23.72[139.162.23.72]...23.239.17.252[23.239.17.252]
     TUNNEL1{36}:  INSTALLED, TUNNEL, reqid 1, ESP SPIs: c4672a30_i c4020d9c_o
     TUNNEL1{36}:   0.0.0.0/0 ::/0 === 0.0.0.0/0 ::/0

root@localhost:~# ip addr show vti1
324: vti1@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1436 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ipip 139.162.23.72 peer 23.239.17.252
    inet 169.254.0.2 peer 169.254.0.1/30 scope global vti1
       valid_lft forever preferred_lft forever
    inet6 fe80::200:5efe:8ba2:1748/64 scope link
       valid_lft forever preferred_lft forever

root@localhost:~# ping 169.254.0.1
PING 169.254.0.1 (169.254.0.1) 56(84) bytes of data.
64 bytes from 169.254.0.1: icmp_seq=1 ttl=64 time=208 ms
64 bytes from 169.254.0.1: icmp_seq=2 ttl=64 time=208 ms
64 bytes from 169.254.0.1: icmp_seq=3 ttl=64 time=208 ms
^C
--- 169.254.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 207.895/207.947/208.039/0.065 ms
```

strongSwanの設定は完了です。

## FRRoutingのインストール

4台あるVPNインスタンスにFRRoutingをインストールし、BGPによる動的ルーティングを構築します。

```
sudo apt install frr
```

BGPを有効化します。

```
sudo sed -i.org 's/bgpd=no/bgpd=yes/' /etc/frr/daemons
```

サービスを再起動します。

```
sudo systemctl restart frr
sudo systemctl enable frr
```

コンソールにログインし、BGPの設定を行います。
コメントが記載されている行を各インスタンスの値に合わせて設定してください。

```cisco_ios
vtysh

Hello, this is FRRouting (version 8.1).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

localhost# conf t
localhost(config)#
! AS番号は各インスタンスの値を設定
router bgp 65001 
 bgp log-neighbor-changes
 no bgp ebgp-requires-policy
 bgp graceful-restart
 timers bgp 3 9
! 同リージョン内のVPNインスタンスのIPとASを設定
 neighbor 10.0.1.101 remote-as 65001
! 対向リージョンのVPNインスタンスのIPとASを設定
 neighbor 169.254.0.1 remote-as 65000 
 !
 address-family ipv4 unicast
! 経路を伝搬するネットワークを設定
  network 10.0.1.0/24
! 同リージョン内のVPNインスタンスのIPを設定
  neighbor 10.0.1.101 next-hop-self
! 同リージョン内のVPNインスタンスのIPを設定
  neighbor 10.0.1.101 soft-reconfiguration inbound
! 対向リージョンのVPNインスタンスのIPを設定
  neighbor 169.254.0.1 soft-reconfiguration inbound
 exit-address-family
exit
!
end
```

BGPの接続が行えていることを確認します。

```cisco_ios
localhost# show ip bgp summary

IPv4 Unicast Summary (VRF default):
BGP router identifier 139.162.23.72, local AS number 65001 vrf-id 0
BGP table version 631
RIB entries 3, using 552 bytes of memory
Peers 2, using 1446 KiB of memory

Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
10.0.1.101      4      65001     37292     34317        0    0    0 1d03h38m            2        2 N/A
169.254.0.1     4      65000     39545     39558        0    0    0 02:05:45            1        2 N/A

Total number of neighbors 2
```

BGPの経路が学習できていることを確認します。

```cisco_ios
localhost# show ip route bgp
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, F - PBR,
       f - OpenFabric,
       > - selected route, * - FIB route, q - queued, r - rejected, b - backup
       t - trapped, o - offload failure

B>* 10.0.2.0/24 [20/0] via 169.254.0.1, vti1, weight 1, 02:05:46
```

設定を保存します。

```
localhost# write memory
Note: this version of vtysh never writes vtysh.conf
Building Configuration...
Integrated configuration saved to /etc/frr/frr.conf
[OK]
```

以上でFRRoutingのインストールは完了です。

## Keepalivedのインストール


4台あるVPNインスタンスにKeepalivedをインストールし、ゲートウェイを冗長化します。

```
sudo apt install keepalived
```

同リージョン内のVPNインスタンスの片方をMaster側に設定します。

```
root@localhost:~# vim /etc/keepalived/keepalived.conf

vrrp_instance Instance1 {
    state MASTER
    interface eth1
    virtual_router_id 10
    priority 100 
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass password
    }
    virtual_ipaddress {
        10.0.1.201←VLAN内で利用していないIPを設定
    }
}
```

同リージョン内のVPNインスタンスのもう片方をBackup側に設定します。


```
root@localhost:~# vim /etc/keepalived/keepalived.conf

vrrp_instance Instance2 {
    state BACKUP
    interface eth1
    virtual_router_id 10
    priority 99
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass password
    }
    virtual_ipaddress {
        10.0.1.201←VLAN内で利用していないIPを設定
    }
}
```

Config Testを行い、何も出力されないことを確認します。（異常がある場合のみ出力されます）

```
keepalived --config-test
```

サービスを起動します。

```
systemctl start keepalived
systemctl enable keepalived
```

Masterに設定した側にvirtual_ipaddressが付与されていることを確認します。

```
root@localhost:~# ip addr show eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 90:de:01:3d:72:5b brd ff:ff:ff:ff:ff:ff
    inet 10.0.1.10/24 brd 10.0.1.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet 10.0.1.201/32 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::92de:1ff:fe3d:725b/64 scope link
       valid_lft forever preferred_lft forever
```

## ICMPインスタンスの設定

疎通確認を行うためのインスタンスに経路の追加設定を行います。

Network設定の書き換えを行うため、上書きされないようにAuto-configure networkingを無効にします。

https://www.linode.com/docs/products/compute/compute-instances/guides/network-helper

無効にした後に設定を追記します。

```
vim /etc/systemd/network/05-eth1.network

[Route]
Destination=10.0.2.0/24←対向のネットワークを記載
Gateway=10.0.1.201←Keepalivedで設定した同リージョン内のIPを記載
```

設定を反映させます。

```
netplan apply
```


## 通信確認

対向の疎通確認用インスタンスにICMPが通るか確認をします。

```
root@localhost:~# ping 10.0.1.5
PING 10.0.1.5 (10.0.1.5) 56(84) bytes of data.
64 bytes from 10.0.1.5: icmp_seq=1 ttl=62 time=213 ms
64 bytes from 10.0.1.5: icmp_seq=2 ttl=62 time=211 ms
64 bytes from 10.0.1.5: icmp_seq=3 ttl=62 time=212 ms
64 bytes from 10.0.1.5: icmp_seq=4 ttl=62 time=211 ms
64 bytes from 10.0.1.5: icmp_seq=5 ttl=62 time=212 ms
64 bytes from 10.0.1.5: icmp_seq=6 ttl=62 time=212 ms
64 bytes from 10.0.1.5: icmp_seq=7 ttl=62 time=213 ms
64 bytes from 10.0.1.5: icmp_seq=8 ttl=62 time=211 ms
^C
--- 10.0.1.5 ping statistics ---
8 packets transmitted, 8 received, 0% packet loss, time 7002ms
rtt min/avg/max/mdev = 211.332/211.946/212.941/0.650 ms
```

今回は速度の測定も実施しています。

```
root@localhost:~# iperf3 -c 10.0.1.5
Connecting to host 10.0.1.5, port 5201
[  5] local 10.0.2.5 port 50990 connected to 10.0.1.5 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   393 KBytes  3.22 Mbits/sec    0    108 KBytes
[  5]   1.00-2.00   sec  5.89 MBytes  49.4 Mbits/sec    0   3.04 MBytes
[  5]   2.00-3.00   sec  12.5 MBytes   105 Mbits/sec    0   6.01 MBytes
[  5]   3.00-4.00   sec  13.8 MBytes   115 Mbits/sec    0   6.01 MBytes
[  5]   4.00-5.00   sec  12.5 MBytes   105 Mbits/sec    0   6.01 MBytes
[  5]   5.00-6.00   sec  13.8 MBytes   115 Mbits/sec    0   6.01 MBytes
[  5]   6.00-7.00   sec  13.8 MBytes   115 Mbits/sec    0   6.01 MBytes
[  5]   7.00-8.00   sec  13.8 MBytes   115 Mbits/sec    0   6.01 MBytes
[  5]   8.00-9.00   sec  13.8 MBytes   115 Mbits/sec    0   6.01 MBytes
[  5]   9.00-10.00  sec  13.8 MBytes   115 Mbits/sec    0   6.01 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   114 MBytes  95.4 Mbits/sec    0             sender
[  5]   0.00-10.21  sec   113 MBytes  92.8 Mbits/sec                  receiver
```

アトランタからシンガポールリージョンの速度は100Mbpsほど出ていることがわかりました。

# まとめ

今回の構成ではアトランタからシンガポールリージョンを接続しましたが、接続先を増やしていくことで、3リージョン、4リージョンと接続数を増やしていくことが可能です。
高可用性が求められるリージョン間通信などでのユースケース等に利用していただければと思います。

# 関連記事

アカマイ・テクノロジーズ合同会社はQiitaでAkamai's cloud computing services関連など開発者向けの記事を掲載しております。

https://qiita.com/xenbase/items/52e2956a64cedc772bd9