IBM Cloud VPCの仮想サーバにOpenVPNで接続する

``※現在はVPCでクライアント・サイト間VPNがβですが使えるようになりましたので、そちらを使用することをお勧めします。``

# 目的

IBM Cloudの仮想サーバにVPNで繋ぎたい場合、クラシックインフラストラクチャーでは標準でSSL-VPNを使用することができましたが、VPCではその仕組みがありません。そのため、VPCでクラシックインフラストラクチャーのようにVPN接続をしたい場合は、一度クラシックインフラストラクチャの仮想サーバにVPNごしにリモートログインし、つぎにTransit Gateway経由でVPCの仮想サーバにリモートアクセスすることになります。それは面倒なので、VPCの仮想サーバにOpenVPNを立てて接続できるようにしてみます。

> VPCにはVPN Gatewayがありますが、これはIPsec VPNでサイト間接続に使用するものですので、個人ユーザーのVPN接続としては使いづらいです。

# 手順

## サーバの前提

* OS: Red Hat Enterprise Linux 8
* パブリックIP: フローティングIP付与済
* 認証: /etc/passwdによるローカル認証

## OpenVPNのインストール

EPELリポジトリを有効にしてOpenVPNとEasyRSAをインストールします。

```
# dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm  
# dnf -y install easy-rsa openvpn
```

## IPフォワーディングの有効化

```/etc/sysctl.d/ipv4-ip-forward.conf
net.ipv4.ip_forward = 1
```

```
# sysctl --system
```

## PKIの設定

### 初期化

```
# /usr/share/easy-rsa/3/easyrsa init-pki

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /root/pki
```

### CAの作成

```
# /usr/share/easy-rsa/3/easyrsa build-ca
Using SSL: openssl OpenSSL 1.1.1c FIPS  28 May 2019

Enter New CA Key Passphrase:     # パスフレーズ
Re-Enter New CA Key Passphrase:  # パスフレーズ（確認）
Generating RSA private key, 2048 bit long modulus (2 primes)
........................................+++++
...................................................................................................................+++++
e is 65537 (0x010001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:Example CA  # 名前

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/root/pki/ca.crt
```

### DHの作成

```
# /usr/share/easy-rsa/3/easyrsa gen-dh
Using SSL: openssl OpenSSL 1.1.1c FIPS  28 May 2019
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
.............................................

DH parameters of size 2048 created at /root/pki/dh.pem
```

### サーバ鍵と証明書の作成

``nopass``とつけることで鍵のパスフレーズをなしにします。

```
# /usr/share/easy-rsa/3/easyrsa build-server-full server nopass
Using SSL: openssl OpenSSL 1.1.1c FIPS  28 May 2019
Generating a RSA private key
........................................................................................................................................................+++++
......+++++
writing new private key to '/root/pki/easy-rsa-8951.WNkyCo/tmp.2IbkNL'
-----
Using configuration from /root/pki/easy-rsa-8951.WNkyCo/tmp.LtjSs2
Enter pass phrase for /root/pki/private/ca.key:  # CA作成時のパスフレーズ
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'server'
Certificate is to be certified until Jan  2 01:24:37 2023 GMT (825 days)

Write out database with 1 new entries
Data Base Updated
```

### 共有鍵の生成

クライアントに配布する共有鍵を生成します。これを使わないこともできますが、信頼された人だけが接続できるように、今回は使用します（ただし共有鍵を安全にユーザーに渡す仕組みが必要です）。

```
#  openvpn --genkey --secret pki/ta.key
```

## サーバ設定

生成したものを配置します。

```
# cp pki/ca.crt pki/ta.key pki/private/server.key pki/issued/server.crt /etc/openvpn/server/
# cp pki/dh.pem /etc/openvpn/server/dh2048.pem
```

設定ファイルのひな型をコピーします。

```
# cp /usr/share/doc/openvpn/sample/sample-config-files/server.conf /etc/openvpn/server/server.conf        
```

必要な設定を行います。

```/etc/openvpn/server/server.conf

# VPNで使用するアドレスレンジ（他で使っているものと重複しないこと）
server 192.168.255.0 255.255.255.0

# VPNを通して直接通信可能にするVPC内のネットワーク
push "route 10.244.0.0 255.255.255.0"

# /etc/passwdでユーザー認証・クライアント証明書は使わない（行末に追加）
plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so login
client-cert-not-required
username-as-common-name
```

## サーバの起動

```
# systemctl enable openvpn-server@server
# systemctl start openvpn-server@server
```

## 1194/udpの許可

FirewalldやVPCのACL・セキュリティグループで1194/udpのインバウンドを許可してください。

## VPNクライアント

### インストール

Windowsユーザーの場合、OpenVPNのクライアントはvpnux Clientがとても簡単です。下記サイトからインストールします。

> https://www.vpnux.jp/

### CA証明書と共有鍵の入手

先ほどサーバで作成した

* ca.crt
* ta.key

を安全な方法で入手します。

## プロファイルの作成

クライアントを起動しプロファイルを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241424/debf0fc2-4237-7f48-06a3-28e5daa22a09.png)

* VPNサーバー: VPNサーバのフローティングIP
* CA証明書: ca.crtを登録
* TLS-Auth HMAC署名を使用: チェック
* 共有鍵: ta.keyを登録
* ID/パスワード認証: VPNサーバの自分のOSユーザーを設定

## 接続

接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241424/6720c204-a297-f56e-df1d-1a9af59a2cdb.png)

## 接続確認

自PCからVPC内のサブネットに通信可能になりました。

```
$ ping 10.244.0.4  # OpenVPNサーバ
PING 10.244.0.4 (10.244.0.4) 56(84) bytes of data.
64 bytes from 10.244.0.4: icmp_seq=1 ttl=64 time=10.4 ms
64 bytes from 10.244.0.4: icmp_seq=2 ttl=64 time=12.8 ms
...

$ ssh ****@10.244.0.4
****@10.244.0.4's password:
[****@**** ~]$
```

しかしこのままではOpenVPNサーバ以外とは通信できません。

```
$ ping 10.244.0.19
（応答なし）
```

OpenVPNサーバ以外とも通信をしたい場合は、OpenVPNクライアントからの通信（192.168.255.0.24）をNATすることで回避できます。

```/etc/nftables/openvpn-nat.nft
#!/usr/sbin/nft -f

table ip nat {
  chain PREROUTING {
    type nat hook prerouting priority -100; policy accept;
  }
  chain INPUT {
    type nat hook input priority 100; policy accept;
  }
  chain POSTROUTING {
    type nat hook postrouting priority 100; policy accept;
    oifname "eth0" ip saddr 192.168.255.0/24 counter packets 1 bytes 84 masquerade
  }
  chain OUTPUT {
    type nat hook output priority -100; policy accept;
  }
}
```

```/etc/sysconfig/nftables.conf
# 行追加
include "/etc/nftables/openvpn-nat.nft"
```

> ちなみにiptablesの場合は下記コマンドに該当します。
> $ iptables -A POSTROUTING -o eth0 -t nat -s 192.168.255.0/24 -j MASQUERADE

これでVPC内の別ホストへも通信可能になりました。

```
$ ping 10.244.0.19
PING 10.244.0.19 (10.244.0.19) 56(84) bytes of data.
64 bytes from 10.244.0.19: icmp_seq=1 ttl=63 time=9.37 ms
64 bytes from 10.244.0.19: icmp_seq=2 ttl=63 time=9.56 ms
```

以上です。