OpenVPNのブリッジモードを利用してVPN上に分散ビルド環境を構築する

弊社では SIE がライセンシー向けに提供している SN-DBS を分散ビルドシステムとして利用しているが、SN-DBS は非IPプロトコルで動作するシステムとなっているため、全開発者が在宅勤務体制となった現状では分散ビルドが機能せず、生産性を大きく落とす状況になっている。
在宅勤務体制がいつまで続くか分からない状況で、分散ビルドが機能しないままでは事業計画に深刻な影響を与えるため、VPNを利用して分散ビルド環境を構築する。

# OpenVPN サーバーを構築

SN-DBS の分差ビルドを動作させるには、コンピューティングリソースが同一ネットワーク上 (L2) で接続されている必要がある。
インターネット上で繋がるシステムはIPアドレスによるルーティングが必要なため L3 で接続され動作しない。
OpenVPN のブリッジモード (TAP) は OpenVPN サーバーが所属するネットワークに仮想的に L2 で接続することが可能。
OpenVPN 互換の AWS Client VPN は TAP モードで作成できず Client to Client な接続もできないため、ES2 インスタンスに OpenVPN を構築する。

## ネットワークとサーバー構成

説明のために構成を書く。必要に応じて変更してください。

```text:VPC
10.10.0.0/16
```

```text:subnet:
10.10.21.0/24
```

```text:route-table:
10.10.0.0/16 => local
0.0.0.0/0 => igw
```

```text:instance
10.10.21.5
```

インスタンス起動時にプライマリIPを指定
サブネットのデフォルトゲートウェイは `10.10.21.1` リゾルバは `10.10.21.2` となる

```text:security-group
TCP 22  0.0.0.0/0
UCP 443 0.0.0.0/0
```

## OpenVPN サーバーの設定

インストール方法や認証方法の設定はググれば出てくるので割愛。

> How To Guide: Set Up & Configure OpenVPN client/server VPN | OpenVPN 
> https://openvpn.net/community-resources/how-to/

```/etc/openvpn/server.conf
port 443
proto udp
dev tap0

ca ca.crt
cert server.crt
key server.key
dh dh2048.pem

server-bridge 10.10.21.5 255.255.255.0 10.10.21.100 10.10.21.255

client-to-client

keepalive 10 120
cipher AES-256-GCM

compress lz4-v2
push "compress lz4-v2"

user nobody
group nogroup

persist-key
persist-tun

status /var/log/openvpn/openvpn-status.log

log         /var/log/openvpn/openvpn.log
log-append  /var/log/openvpn/openvpn.log

verb 3

explicit-exit-notify 1
```
サーバーのアドレス `10.10.21.5`
サブネットマスク `255.255.255.0`
クライアントに割当てるアドレス `10.10.21.100 ~ 10.10.21.255`

```
server-bridge 10.10.21.5 255.255.255.0 10.10.21.100 10.10.21.255
```


## ネットワークブリッジを作成

仮想ネットワークインターフェイス `tap0` と物理ネットワークインターフェイス `ens5` をブリッジで接続する。
また、VPNクライアントへのデータフレーム（パケット）はサーバーが転送する必要があるため、OpenVPN サーバー宛以外のフレームを NIC で破棄されないようにプロミスキャスモードを設定する。

これらの設定は途中でインスタンスのネットワーク接続が切れるので、スクリプトで一気に行う。
テスト中に ssh コネクションが切れたら、コンソールからインスタンスを再起動すると良い。

> Ethernet Bridging | OpenVPN 
> https://openvpn.net/community-resources/ethernet-bridging/

今回は Ubuntu Server 18.04 で作成しているので、 `netplan` と `systemd-networkd` を利用してブリッジを構築する。
ブリッジの作成には `bridge-utils` を利用する。

```
ens5: 物理NIC
tap0: 仮想NIC
br0 : 作成するブリッジ
```

### netplan の設定

物理 NIC に設定されている macaddress をブリッジ側に設定する。

```yaml:/etc/netplan/50-cloud-init.yaml
# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    version: 2
    ethernets:
        ens5:
            dhcp4: false
    bridges:
        br0:
            interfaces: [ens5]
            dhcp4: true
            macaddress: 06:85:47:07:47:ac
```

### networkd-dispatcher の設定

netplan はスクリプト起動をサポートしない代わりに、networkd-dispatcher を利用したスクリプト呼び出しが利用できる。 
> FAQ | Netplan 
> https://netplan.io/faq


```sh:/etc/networkd-dispatcher/routable.d/50-ifup-hooks
#!/bin/sh

openvpn --mktun --dev tap0
ifconfig tap0 0.0.0.0 promisc up
ifconfig ens5 0.0.0.0 promisc up
brctl addif br0 tap0
```

実行権限も付けておく。

### ネットワークインターフェイスの確認
```
$ brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.0685470747ac       no              ens5
                                                        tap0
```
```
$ ifconfig
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.21.5  netmask 255.255.255.0  broadcast 10.10.21.255
        inet6 fe80::485:47ff:fe07:47ac  prefixlen 64  scopeid 0x20<link>
        ether 06:85:47:07:47:ac  txqueuelen 1000  (Ethernet)
        RX packets 341095  bytes 267201788 (267.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 308550  bytes 263056027 (263.0 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens5: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST>  mtu 1500
        inet6 fe80::485:47ff:fe07:47ac  prefixlen 64  scopeid 0x20<link>
        ether 06:85:47:07:47:ac  txqueuelen 1000  (Ethernet)
        RX packets 320874  bytes 268137556 (268.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 329184  bytes 266967804 (266.9 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 34370  bytes 2746382 (2.7 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 34370  bytes 2746382 (2.7 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

tap0: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST>  mtu 1500
        inet6 fe80::c857:6eff:fe4a:d347  prefixlen 64  scopeid 0x20<link>
        ether ca:57:6e:4a:d3:47  txqueuelen 100  (Ethernet)
        RX packets 20388  bytes 3858461 (3.8 MB)
        RX errors 0  dropped 7  overruns 0  frame 0
        TX packets 201  bytes 9586 (9.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
```

# クライアントの設定

```
client
dev tap0
proto udp
remote SERVER_ADDRESS 443
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
verb 3
<ca>
-----BEGIN CERTIFICATE-----
MIIDSzC...
-----END CERTIFICATE-----
</ca>

cert /path/to/client.crt
key /path/to/client.key
```

### ルーティングテーブルを確認

`10.10.21.0/24` への経路情報があるか確認

```
% netstat -nr
Routing tables

Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
default            10.22.100.1        UGSc           91        7     en0       
10.10.21/24        link#12            UC              4        0    tap0      !
10.10.21.5         link#12            UHLWI           0        0    tap0      !
10.10.21.101       f2:91:9a:c:1c:78   UHLWI           0        1     lo0       
10.10.21.255       ff:ff:ff:ff:ff:ff  UHLWbI          0        1    tap0      !
10.22.100/24       link#5             UCS             9        0     en0      !
10.22.100.1/32     link#5             UCS             1        0     en0      !
10.22.100.1        0:3a:9d:4e:4c:1b   UHLWIir        31       78     en0   1145
10.22.100.116/32   link#5             UCS             1        0     en0      !
10.22.100.116      34:36:3b:d3:16:a   UHLWI           0        6     lo0       
10.22.100.255      ff:ff:ff:ff:ff:ff  UHLWbI          0        6     en0      !
127                127.0.0.1          UCS             0        0     lo0       
127.0.0.1          127.0.0.1          UH             75   562099     lo0       
169.254            link#5             UCS             1        0     en0      !
224.0.0/4          link#5             UmCS            2        0     en0      !
224.0.0.251        1:0:5e:0:0:fb      UHmLWI          0        0     en0       
239.255.255.250    1:0:5e:7f:ff:fa    UHmLWI          0      693     en0       
255.255.255.255/32 link#5             UCS             1        0     en0      !
255.255.255.255    ff:ff:ff:ff:ff:ff  UHLWbI          0        6     en0      !
```

### 疎通確認
```
% ping google.com
PING 10.10.21.5 (10.10.21.5): 56 data bytes
64 bytes from 10.10.21.5: icmp_seq=0 ttl=54 time=14.021 ms
64 bytes from 10.10.21.5: icmp_seq=1 ttl=54 time=111.575 ms
64 bytes from 10.10.21.5: icmp_seq=2 ttl=54 time=27.658 ms
```

# 分散ビルドを試す

![dbs0.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083/b21274ed-407a-bec5-f5a1-4cc169730b73.png)

![dbs1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083/003b60b6-2858-18fa-1e0b-e21536938e7c.png)

動いた。

AWS のネットワーク上で動いているので、EC2 インスタンスを利用してエージェントを増やすことも出来ますね！