OpenVPNサーバ構築(CentOS7) for Android(tun)

(2020.03.03)server.confの置き場所とta.keyのパス指定、およびsystemctlへのサービス登録が適切でなかった為、追記変更しました
(2020.03.05)SELinuxの項目の表現が正しくなかったので追記変更しました。

Android端末から接続できるopenvpnサーバを自前で立てたいケースがあったので記載します。理解が曖昧な所が多かったので調べながら書いていきます。

VPNサーバの先に別セグメントのネットワーク（ファイルサーバ等）があり、そこにも接続したいとします。取り急ぎのテレワーク用などに。

この記事を書いている時点でAndroid向けOpenVPNクライアントはtun方式(ルータモード)のみ対応となっていますので、その方式で構築します。

記事中の下記項目は、適宜自環境のものに読み替えてください。

+ VPNサーバ
 + IPアドレス(192.168.1.1)
 + ポート番号(11940) ※デフォルトから変更しています。
+ VPNサーバ側ネットワーク(192.168.1.0/24)
+ VPN側ネットワーク(10.8.0.0/24)
+ ファイルサーバネットワーク(192.168.2.0/24)

手順概要
---
1.OpenVPNとeasy-rsaパッケージのインストール
2.認証局の設置とサーバ・クライアントの証明書作成
3.OpenVPNサーバ設定・サービス起動
4.IPフォワード(ルータ化)設定
5.firewall設定(ポート開放・IPマスカレード)
6.SELinux設定
7.クライアント設定と動作確認

参考にさせて頂いたURL
---
https://www.openvpn.jp/document/
https://wiki.archlinux.jp/index.php/Easy-RSA
https://qiita.com/horus19761108/items/9c7879149218d9325c5e
https://mano.xyz/2008/
https://fuketch.wordpress.com/2013/02/27/openvpn%E3%81%A8selinux%E3%81%AE%E3%81%8A%E8%A9%B1/

手順
---
####1.OpenVPNとeasy-rsaのインストール
yumにepelリポジトリを追加します。

```
# yum install epel-release -y
```

epelリポジトリが有効になったらopenvpnとeasy-rsaパッケージをインストールします

```
# yum install openvpn -y
# yum install easy-rsa -y
```

今回インストールした各パッケージのバージョン

```
# yum list openvpn
インストール済みパッケージ
openvpn.x86_64                                      2.4.8-1.el7                                       @epel

# yum list easy-rsa
インストール済みパッケージ
easy-rsa.noarch                                      3.0.6-1.el7                                      @epel
```

####2.認証局の設置とサーバ・クライアントの証明書作成
> セキュリティ上の理由から CA とするマシンは OpenVPN を実行するマシンとは分けることが推奨されます。 

とありますが今回は端折ってサーバを分けずに作業します。

作業用ディレクトリを`/etc/openvpn/`の配下に作成し、以下そこで作業します。

```
# mkdir /etc/openvpn/easy-rsa
# cp /usr/share/easy-rsa/3.0.6/* /etc/openvpn/easy-rsa -R 
```

コピー元フォルダ`/3.0.6/`の箇所はインストールしたeasy-rsaバージョンに基づいて変更してください。

作業フォルダに移動し、必要なファイルを作成していきます。

```
# cd /etc/openvpn/easy-rsa
```

#####(1)初期化

```
# ./easyrsa init-pki
```

`./pki/`ディレクトリが作成されます。

これからサーバ動作に必要な下記のファイルを作成していきます。
`.crt`は公開鍵証明書、`.key`は秘密鍵です。

+ 認証局用ファイル（本来は別サーバとするべき）
 + `ca.crt`
 + `ca.key`
+ Diffie-Hellman (DH) 暗号化用パラメータファイル
 + `dh.pem`
+ サーバー用ファイル
 + `server.crt`
 + `server.key`
+ TLS認証用Hash-based Message Authentication Code (HMAC) 鍵
 + `ta.key`


※`easy-rsa v2`では上記ファイルを作成する為のスクリプトが個別に存在していましたが、`v3`からは各スクリプトがなくなり、代わりに`easyrsaコマンド＋引数`に集約されています。`vars`の編集＆実行も不要になりました。


#####(2)認証局の作成

認証局秘密鍵のパスワードはサーバやクライアントの作成（署名）をする際に都度必要になります。忘れず控えてください。

```
# ./easyrsa build-ca
Enter New CA Key Passphrase: (秘密鍵のパスワード入力)
Re-Enter New CA Key Passphrase: (秘密鍵のパスワード確認入力)
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:　(任意の名称を入力)
```

#####(3)DHパラメータ作成

デフォルト桁数は2048なので変更せず作成します。

```
# ./easyrsa gen-dh
```

#####(4)サーバ用ファイル作成

例ではサーバ用の秘密鍵にパスワードは指定しません(nopass)。指定するとサーバ起動時にパスワードを要求されます。セキュリティは保てますがサービス起動が面倒になるので適宜判断してください。

```
# ./easyrsa build-server-full server nopass
Enter pass phrase for /etc/openvpn/easy-rsa/pki/private/ca.key: (認証局秘密鍵のパスワード入力)
```

2つ目の引数`server`は任意の名称です。上記の指定の場合は`server`というファイル名で証明書と秘密鍵が作成されます。
また、(2)で認証局の秘密鍵にパスワードを指定しているのでそれを入力します。

成功すると`./issued/server.crt`と`./private/server.key`が作成されます。

#####(5)クライアント用ファイル作成

```
# ./easyrsa build-client-full client01 nopass
Enter pass phrase for /etc/openvpn/easy-rsa/pki/private/ca.key: (認証局秘密鍵のパスワード入力)
```

成功すると`./issued/client01.crt`と`./private/client01.key`が作成されます。必要なクライアント数分、`client01`の箇所を変えてファイルを作成します。

クライアント起動時にパスワード入力を必要にしたい場合は`nopass`オプションを付けずに実行します。以下のように聞かれます。

```
Enter PEM pass phrase: (クライアント用パスワード入力)
Verifying - Enter PEM pass phrase: (クライアント用パスワード確認入力)
```

####3.OpenVPNサーバ設定・サービス起動

#####(1)TLS認証で使用する静的鍵の作成

コネクション時にTLS認証を実施する為の準備。

>  以下の攻撃から防護されます:
>    ・ポートスキャン。
>    ・OpenVPN の UDP ポートに対する DOS 攻撃。
>    ・未権限のマシンからの SSL/TLS ハンドシェイク。
>    ・SSL/TLS 実装のバッファオーバーフロー脆弱性に対する攻撃。

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

ta.keyはクライアントでも使用するので取り扱い注意。


#####(2)サーバ設定作成
サンプルから設定を作成します。`openvpn-2.4.8`の箇所はインストールしたバージョンによって読み替えてください。

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

以下、`/etc/openvpn/server/server.conf`を編集します。
細かい箇所は端折り、最低限の設定のみ抜き出して記載します。

```/etc/openvpn/server/server.conf
# 待ち受けポート、デフォルトは1194
port 11940
# 使用プロトコル TCPはUDPよりも大幅にスループットが低下します。
proto udp
# Androidから接続したいのでtun
dev tun
# 各必要ファイルのパスを指定
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
# VPNのネットワークアドレス指定(重複の可能性のないもの)
server 10.8.0.0 255.255.255.0 
# 配布用IPアドレスのテーブル(固定IPを使用したい場合はipp.txtを編集)
ifconfig-pool-persist ipp.txt 
# クライアントにpushされるルーティング情報（サーバ側のLANセグメント）
push "route 192.168.1.0 255.255.255.0"
# クライアントにpushされるルーティング情報（サーバより先のLANセグメントがある場合）
push "route 192.168.2.0 255.255.255.0"
# クライアント間通信を許可したい場合
client-to-client 
# 10秒ごとに応答確認、120秒応答なしで切断
keepalive 10 120 
# 暗号化アルゴリズム指定 とりあえず以下で
cipher AES-256-CBC
# TLS認証の有効化と使用する静的鍵を指定
tls-auth /etc/openvpn/ta.key 0
# 圧縮の有効
comp-lzo 
# セキュリティ強化の為に実行グループを変更
user nobody
group nobody
# 再起動時の秘密鍵とtunデバイスの保持設定 基本的に有効化する
persist-key
persist-tun
# ログの出力先
status /var/log/openvpn-status.log
log /var/log/openvpn.log
```

ここまで完了すると、サーバの起動ができるようになります。

```
# systemctl start openvpn-server@server.service
```

サービス起動したい場合は以下

```
# systemctl enable openvpn-server@server.service
```


`@`以降は`/etc/openvpn/server/`に配置してあるコンフィグのファイル名（拡張子を除く）＋`.service`と指定します。

###4.IPフォワード(ルータ化)設定
今回、クライアントからVPNサーバのネットワーク`192.168.1.0/24`の先にあるネットワーク`192.168.2.0/24`にも接続したい為、サーバをルータ化します。尚、VPNサーバの先にあるネットワークへのルーティングはサーバに設定済みとします。

```text:自ネットワークと宛先ネットワークの確認
# ip a
x: (IF名): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global (IF名)

# ip route
192.168.2.0/24 via (ゲートウェイIP) dev (IF名)  proto static  metric 100
```

尚、CentOS7ではネットワークインタフェースが異なるネットワークにパケットが通過しない模様。その為`rp_filter（Reverse Path Filtering）`というものを無効化します。例では`all`としていますが実際にはインタフェース名(`eth0`とか)を適宜指定してください。

```/etc/sysctl.conf
# ルータ化
net.ipv4.ip_forward = 1
# rp_filterの無効化
net.ipv4.conf.all.rp_filter = 2
```

設定の反映をします。

```
# systemctl restart systemd-sysctl
```


###5.firewall設定(ポート開放・IPマスカレード)
サーバ設定で待ち受けポートをデフォルトのudp 1194からudp 11940に変更しているのので、`firewalld`でOpenVPN用のポートをopenにします。また、クライアントからVPNサーバ側ネットワークに接続したいので、IPマスカレードを有効化します。


```text:ゾーン名の確認
# firewall-cmd --list-all
(ゾーン名) (active)
  interfaces: (IF名)
  sources:
  services: dhcpv6-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:
```

ゾーンのudp 11940を開放とIPマスカレードの有効化。`--permanent`オプションで永続化します。

```text:設定の反映と確認
# firewall-cmd --add-port=11940/udp --zone=(ゾーン名) --permanent
# firewall-cmd --add-masquerade --zone=(ゾーン名) --permanent
# firewall-cmd --reload
# firewall-cmd --list-all
(ゾーン名) (active)
  interfaces: (IF名)
  sources:
  services: dhcpv6-client ssh
  ports: 11940/udp
  masquerade: yes
  forward-ports:
  icmp-blocks:
  rich rules:
```

`ports: 11940/udp`と`masquerade: yes`を確認。VPNサーバ側ネットワークのノードからは、VPNクライアントのIPはVPNサーバのIP`192.168.1.1`として見えるようになります。

###6.SELinux設定
デフォルトポートを変更した事をSELinuxで許可する必要があります。SELinuxを無効化している方やデフォルトポート1194で動作する場合は読み飛ばしてください。

```
# getenforce
Enforcing
```

EnforcingならSELinuxが有効化状態です。

```text:ポリシー編集ユーティリティのインストール
# yum -y install policycoreutils-python
```

```text:SELinuxがopenvpnに許可しているポートを確認
# semanage port -l |grep openvpn
openvpn_port_t                 tcp      1194
openvpn_port_t                 udp      1194
``` 

openvpn_port_t というのは openvpnプロセスが使用して良いportを定義している設定名です。

```text:今回使用するopenvpnのudpポートを追加
# semanage port -a -t openvpn_port_t -p udp 11940
```

```text:openvpn_port_tにudp11940が追加された事を確認
# semanage port -l |grep openvpn
openvpn_port_t                 tcp      1194
openvpn_port_t                 udp      11940, 1194
```

SELinuxは無効化しがちですが運用できる環境であれば使った方が良いと思います。SELinuxで弾かれたアクセスは以下のコマンドで確認できます。トラブルシューティングの際に。

```
# ausearch -m AVC
```


###7.クライアント設定と動作確認
一応、nmapでポート開放を確認しておきます。udpポートをスキャンする場合は`-sU`オプションを付けます。

```
# nmap 192.168.1.1 -p 11940 -sU
PORT      STATE         SERVICE
11940/udp open|filtered unknown
```

udpの場合はopenではなくopen/filteredとなるとの事。closeだった場合はサーバが正しく起動していないと思われます。

#####設定ファイルと必要ファイルのコピー
クライアントには以下のファイルが必要です。安全な方法でサーバからコピーしてきてください。括弧内にこれまでの例で作業した場合のパスを記載します。

+ 認証局証明書ファイル
 + `ca.crt`(/etc/openvpn/easy-rsa/pki/)
+ TLS認証用Hash-based Message Authentication Code (HMAC) 鍵
 + `ta.key`(/etc/openvpn/)
+ クライアント証明書、秘密鍵
 + `client01.crt`(/etc/openvpn/easy-rsa/pki/issued/)
 + `client01.key`(/etc/openvpn/easy-rsa/pki/private/)
+ クライアント設定ファイル（サンプルを持ってくる）
 + `client.conf`(/usr/share/doc/openvpn-2.4.8/sample/sample-config-files/)

WindowsやAndroidがクライアントの場合、設定ファイルの拡張子を`.conf`から`.ovpn`に変更する必要があるかもしれません。拡張子を変更したらテキストエディタ等で開き以下の通り設定します。必要最低限の設定箇所のみ記載します。各々、サーバの設定にあわせて下さい。

```client.ovpn
# 使用デバイスやプロトコルはサーバとあわせて
dev tun
proto udp
remote (サーバIPまたはドメイン名) 11940
# 証明書や秘密鍵のファイル名
ca ca.crt
cert client01.crt
key client01.key
# TLS認証に使用する静的鍵のファイル名
tls-auth ta.key 1
# 暗号化、圧縮の方式もサーバとあわせて
cipher AES-256-CBC
comp-lzo
```

Windowsの場合はOpenVPNインストールフォルダ配下のconfigフォルダに上記をコピーします。但しProgram Files配下のフォルダ内で直接上記の編集・上書き保存しようとするとUACが働き、`%userprofile%\appData\Local\VirtualStore\`フォルダに退避されてしまい設定が反映しません。一度別の作業フォルダ内で編集し、編集後の設定ファイルをconfigフォルダにコピーすると良いです。

Androidの場合は上記編集を終わらせておき、必要ファイルと一緒にAndroid端末に（セキュアな方法で）コピーします。その後、Google Playから[OpenVPN for Android](https://play.google.com/store/apps/details?id=de.blinkt.openvpn&hl=ja)をインストールします。インストールしてできたアプリアイコンを起動し、IMPORT PROFILE→FILEと選択し、コピーしたclient.ovpnを選択しIMPORTします。Profile Successfully Importedと出たら、右上の「ADD」をクリックします（最後のADDを忘れやすい）。


---
以上で完了です。VPNサーバがルータにもなる為、pushコマンドが正常に反映すればVPNサーバを超えたネットワークの先にも接続ができます。

WindowsのOpenVPNクライアントは管理者権限で実行してください。でないとpushコマンドによるroute反映が失敗し、VPNへの接続ができません。