[AWS] 積年の夢の端緒 ～初心に還ってOpenVPN～

# プロローグ
思えば、自分がAWS上でのVPNに触れたのは、数年前に出逢った[こちらの記事](http://qiita.com/hideki/items/657a5701133e713fcf8c)がきっかけでした。
今や、[SoftEther](http://qiita.com/coga2000/items/668081e6e42ce313d32c)/[WindowsVPN](http://qiita.com/coga2000/items/41edef9b7dee2f997a1a)/[VyOS](http://qiita.com/coga2000/items/4555ef13cae70c69f677)の経験を経て、思えば遠くに来たもんだ。
このたび、一周回って、OpenVPNをSSL-VPN(Point-to-Site VPN/SW-VPN)として改めてAWS上で利用する機会がありまして、今自分が具備する経験・知識を総動員して、下記にトライしました：

- OpenVPNサーバ(EC2)に挿すNIC(ENI)を、PublicとPrivateで分けて2枚に！
- CloudFormationでの全自動化！
    - OpenVPNサーバのインストール
    - CA作成～ファーストユーザの証明書/秘密鍵作成
    - CRL作成（dummyユーザを作成＆削除）
    - ビルトインのec2-userを削除し、別のVPN管理者ユーザを作成（SSH接続＆sudo利用、おっけー）
    - OpenVPNクライアント用の設定ファイルのテンプレートの出力
    - 【未着手】~~FlowLogs用設定投入（LogStream/LogGroup作成＆IAM-Role作成）~~
    - 【未着手】~~OpenVPNログのローテーション機能を追加~~
- 敢えてのOpenVPN Clientの利用（お気に入りのvpnuxClientもおっけー）
- Man-in-the-Middle対策（よりセキュアに）
- [CRYPTRECの推奨暗号リスト](https://www.cryptrec.go.jp/list.html)や[IPAの暗号推奨期間](https://www.ipa.go.jp/files/000013413.pdf)を受けて、AES256/SHA256(SHA1は非推奨)の採用（よりセキュアに）

⇒以下に挙げる設定値等に関して、こうしたらどう？とか、それはアカン！とかあったら、ご指摘いただけると幸いです。

# 環境構築手順
別にCloudFormationでなくても、下記の手順を踏めばおっけー。
（CloudFormationファイル(YAML)にご興味ある方、個別にご連絡いただければ♬）
①必要なSWのインストール
②OpenVPNサーバの設定
③OpenVPNサーバの環境構築手順（スクリプト）
④OpenVPNクライアントの設定

## ①必要なSWのインストール
Amazon Linuxであれば、下記でおっけー：

```
# yum update -y
# yum install ec2-net-utils -y
# yum install tcpdump -y (任意/オマケ)
# yum install openvpn -y
# yum install easy-rsa --enablerepo=epel -y
```

※CloudFormation中では「runcmd:」で実行しています。

## ②OpenVPNサーバの設定
下記の『server.conf』でおっけー：

```ruby:server.conf
port  443
proto tcp
dev   tun

server 《VPNに割り当てるCIDR》 255.255.255.0
ifconfig-pool-persist ipp.txt

push "route 《プライベートサブネットのCIDR》 255.255.255.0"  # Example

ca         ca.crt      # Certificate Authority
cert       server.crt  # Server Certificate
key        server.key  # Server Private Key
dh         dh2048.pem  # DH parametar key for DH key exchange
crl-verify crl.pem     # Certificate Revocation List
tls-auth   ta.key 0    # Symmetric key for TLS-Auth ("0" means "server")

cipher AES-256-CBC  # should match with client-side
auth SHA256         # should match with client-side

keepalive 10 60
ping-timer-rem
persist-tun
persist-key
comp-lzo
max-clients 30

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

user  nobody
group nobody

verb 3
```

## ③OpenVPNサーバの環境構築手順（スクリプト）
下記の『openvpn_config.sh』を実行すればおっけー：

```ruby:openvpn_config.sh
#!/bin/sh
export OPENVPN_CONFIG=/etc/openvpn
export KEY_HOME=/usr/share/easy-rsa/2.0
cd $KEY_HOME

# "Overwriting" env parameters in "vars" here
echo 'export KEY_COUNTRY="JP"' >> $KEY_HOME/vars
echo 'export KEY_PROVINCE="XXX"' >> $KEY_HOME/vars
echo 'export KEY_CITY="XXX"' >> $KEY_HOME/vars
echo 'export KEY_ORG="XXX"' >> $KEY_HOME/vars
echo 'export KEY_EMAIL="XXX@YYY.ZZZ"' >> $KEY_HOME/vars
echo 'export KEY_OU="IT Admin"' >> $KEY_HOME/vars

echo 'export KEY_SIZE=2048' >> $KEY_HOME/vars
echo 'export CA_EXPIRE=3650' >> $KEY_HOME/vars   # [Days]
echo 'export KEY_EXPIRE=3650' >> $KEY_HOME/vars  # [Days]

source $KEY_HOME/vars
$KEY_HOME/clean-all                     # Initialize CA
$KEY_HOME/pkitool --initca              # Build CA (in non-interactive mode)
$KEY_HOME/pkitool --server server       # Build server certificate & private key (in non-interactive mode)
$KEY_HOME/build-dh                      # Build DH parametar (prime number?) for DH key exchange

$KEY_HOME/pkitool $* dummy              # Preparation for generating crl file (creating "dummy" user cert)
$KEY_HOME/revoke-full dummy             # Generate crl (Certificate Revocation List)

openvpn --genkey --secret $OPENVPN_CONFIG/ta.key       # Generating tls-auth key (symmetric)

ln -s $KEY_HOME/keys/ca.crt     $OPENVPN_CONFIG
ln -s $KEY_HOME/keys/server.crt $OPENVPN_CONFIG
ln -s $KEY_HOME/keys/server.key $OPENVPN_CONFIG
ln -s $KEY_HOME/keys/dh2048.pem $OPENVPN_CONFIG

# crl.pem is specially treated.
mv $KEY_HOME/keys/crl.pem $OPENVPN_CONFIG
chmod 666 $OPENVPN_CONFIG/crl.pem
ln -s $OPENVPN_CONFIG $KEY_HOME/keys/crl.pem    

# Add a new OS/SSH user "vpnadmin" instead of the built-in user "ec2-user".
/usr/sbin/useradd vpnadmin
/usr/sbin/usermod -G wheel vpnadmin
mkdir /home/vpnadmin/.ssh
chown vpnadmin:wheel /home/vpnadmin/.ssh
chmod 700 /home/vpnadmin/.ssh
cp /home/ec2-user/.ssh/authorized_keys /home/vpnadmin/.ssh/authorized_keys
chown vpnadmin:wheel /home/vpnadmin/.ssh/authorized_keys
chmod 600 /home/vpnadmin/.ssh/authorized_keys
$KEY_HOME/pkitool $* vpnadmin

# Delete the built-in user for security
/usr/sbin/userdel -r ec2-user   

sysctl -w net.ipv4.ip_forward=1                     # Transient config
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf  # Permanent config

# IP masquerade (from VPN clients to AWS private servers)
iptables -F
iptables -t nat -F
iptables -X
iptables -t nat -A POSTROUTING -s 《VPNクライアントのCIDR》 -d 《プライベートサブネットのCIDR》 -j MASQUERADE

/sbin/chkconfig --add openvpn
service openvpn start
```

**特記事項**としては、『外部からの通信をAWSが受け付ける』ために、下記の(A)か(B)が必要：
**(A)SecurityGroupで通信を許可する。**
⇒Allow From 《VPNクライアントのCIDR》
**(B)IPマスカレードの設定を投入する。**
⇒《VPNクライアントのCIDR》からの通信を、OpenVPNサーバから来た通信に見せる（マスカレード＝仮面舞踏会、♬きらめく光～）：

```
iptables -t nat -A POSTROUTING -s 《VPNクライアントのCIDR》 -d 《プライベートサブネットのCIDR》 -j MASQUERADE
```
⇒AWSの仕様/制約に、「AWSが把握していない(払い出したのではない)IPアドレスは受け付けない(ドロップする)」というルールに基づく。
⇒(A)/(B)どちらが「正解」かはわからない。
⇒(A)/(B)いずれもおっけーなことは確認済み。
⇒「トレーサビリティ(A)」と「疎結合(B)」のいずれを取るかのトレードオフ。
⇒今回の自動化では（敷居がHigherな）「疎結合(B)」を採用。

また、「ビルトインユーザec2-userの削除」は、ec2-userでSSHログインしている場合はエラーが起こる（自分自身を消そうとする）ので、別ユーザ（vpnadmin等）でSSHログインしなおしてから実行する必要がある。
（CloudFormationならイキナリvpnadminでの接続になる）


## ④VPNクライアントの設定
**(甲) OpnVPNクライアントの場合**
下記の設定ファイル『client.conf』でおっけー。

```ruby:client.conf
client
dev tun
proto tcp

remote 《OpenVPNサーバのEIP》 443

# Assuming the certificate files below are put in the same folder as the client config file:
ca       ca.crt        # Certificate Authority
cert     vpnadmin.crt  # Client Certificate
key      vpnadmin.key  # Client Private Key

remote-cert-tls server
;ns-cert-type server   # Will be deprecated
tls-auth ta.key 1      # ("1" means "client")

cipher AES-256-CBC     # should match with server-side
auth SHA256            # should match with server-side

comp-lzo
nobind
persist-key
persist-tun
resolv-retry infinite

verb 3
```

⇒OpenVPNクライアントからOpenVPNサーバとの接続コマンドは、コマンドプロンプトから下記のコマンドを一発（GUIとかもありますが）：

```
C:\> openvpn --config client.ovpn
```

※注意点は、コマンドプロンプトを管理者権限で起動すること（スタティックルートを追加するため）

⇒ログがずるずる出て、『Initialization Sequence Completed』が出たらおっけー。

**(乙) vpnuxClientの場合**
    下記の設定画面（2画面）でおっけー。

**設定画面①**
![vpnuxClient設定①.jpg](https://qiita-image-store.s3.amazonaws.com/0/48232/05a5c6b2-1869-2ea8-a3a4-854c078bc010.jpeg)

**設定画面②**
![vpnuxClient設定②.jpg](https://qiita-image-store.s3.amazonaws.com/0/48232/5f0aa03b-0e90-1e39-fe1f-76df96a00702.jpeg)

⇒ログがずるずる出て、『Initialization Sequence Completed』が出たらおっけー。

# 小咄：crl.pemファイルが読めなくてハマった噺ぃ~
当初、「crl.pem（証明書失効リスト）」ファイルも、「server.*」ファイルと同様に、EasyRSAで作成したディレクトリに存在するマスタファイルに対して/etc/openvpn(=OPENVPN_CONFIG)からシンボリックリンクを張るアプローチでした：

```
# ln -s $KEY_HOME/keys/crl.pem $OPENVPN_CONFIG
```

が、これだと『crl.pem cannot read』というエラーが発生する。パーミッションが原因だけど、$KEY_HOME/keysフォルダの権限も開放しないといけない？そこで、

```
# mv $KEY_HOME/keys/crl.pem $OPENVPN_CONFIG
# chmod 666 $OPENVPN_CONFIG/crl.pem
# ln -s $OPENVPN_CONFIG $KEY_HOME/keys/crl.pem
```

と変更したところグー。
「server.\*」ファイルなんかもOPENVPN_CONFIGへmvしようかどうか、悩ましいところです。
（一番更新されうるだろうcrl.pemをmvしているから、基本不変なserver.\*ファイルも。。。）

# エピローグ
...SSL-VPNは、OpenVPNでもvpnuxClientでもそうだけど、コネクション初期化フェーズをログでtailしているときに、疎通が成功するときはログがずるずるスムーズに流れて『**Initialization Sequence Completed**』が登場する。これがカタルシスっっっ☆ ☆ ☆
（うまくいかないときは、『Restarting』やらなんやらでグダグダ）

# 参考情報
1. [AWS環境にOpenVPNでVPNを構築](http://qiita.com/hideki/items/657a5701133e713fcf8c)
⇒ワタクシのあらゆるVPN知識の産みのヲヤ。

2. [基本的なOpenVPNの構築手順](http://qiita.com/makisyu/items/2ac579c0e169970b1609)
⇒特にta.keyについて。EasyRSA 3.0は未着手。。。

3. [OpenVPN サーバ構築](https://www.agilegroup.co.jp/technote/install-openvpn.html)
⇒本格的ですごい。。。脱帽。

4. [OpenVPN クライアント設定](https://www.agilegroup.co.jp/technote/openvpn-client.html)
⇒3.のクライアント設定編。

5. [iptablesで特定のポートを別のホストへ転送する方法](https://mo.kerosoft.com/0203)
⇒特にNATテーブル（IPマスカレード）の設定。

6. [sudoers は編集せずに sudoers.d の中に設定を書こう](http://qiita.com/ngyuki/items/88d0f71601e05f239750)
⇒特にsudoersの追加を自動化する方法。