TORIPIYO DIARY

recent events, IoT, programming, security topics

AWS環境にAmazon Linux 2でOpenVPNサーバを構築・運用する

以前、AWS環境にOpenVPNサーバを構築して運用する記事を書きました。

toripiyo.hatenablog.com

この記事は、Amazon Linuxでの構築を前提に書かれているのですが、いまはAmazon Linux 2が主流となっています。そこで、Amazon Linuxから、Amazon Linux 2向けに記事を書き直してみました。

目次

どんな方式でVPNサーバを構築するか

以下記事の通り、VPN通信を実現するには様々な方法(PPTP, L2TP/IPsec, SSTP, IKEv2, )があります。

proprivacy.com

今回も以前の記事と同じように、TLSを利用したOpenVPNの証明書認証方式でVPNサーバを構築します。

VPNの認証方式を証明書認証にすることで、ユーザごと個別に証明書を有効・無効にしてアクセスの許可・拒否を設定出来ます。 パスワード認証の方が設定は楽ですが、退職者が出るたびに新しいパスワードを設定して、VPN利用者全員にパスワードの更新を周知する手間が生じるので、長期的に見れば証明書認証の方が運用は楽になります。

AWSVPC上にVPNサーバを構築することで、以下のようなケースで利用できるようになります。

  • EC2とのSSH通信をVPNサーバ経由でネットワークを閉じて出来るようになるので、SSH通信をしたいサーバにパブリックIPを付与する必要がなくなります。SSHの入り口を外部から無くせるので、外部からの脅威を軽減できます。
  • AWS上に構築した社内向けWEBアプリを、プライベートIPのみの閉じたネットワークで公開出来るようになります。パブリックIPを持たないので外部から社内ツールに侵入される脅威を軽減できます。(もちろん、たとえ閉じたネットワークでも認証・権限管理、監査機能をアプリ側にちゃんと実装する方が安心です。)

構築手順

構成図

以下のネットワーク構成を想定して、VPNサーバを構築します。

f:id:ha107chan:20200514211321p:plain
VPNネットワーク構成図

  • 複数のVPCがあるのであれば、別途VPC Peeringを設定することで、VPNサーバの置かれたVPCとは異なるVPC上のサーバとも通信が出来ます。
  • VPNを経由してインターネットに接続するようにしたい場合は、VPNサーバの所属するsubnetのルートテーブルにインターネットゲートウェイなどを設定して、外部通信できるようにしておいてください。

AWSの設定

セキュリティグループの作成

VPNサーバにアタッチするセキュリティグループを作成します。セキュリティグループの名前は自分の好きな名前で大丈夫です。

セキュリティグループのInboundルールに、

  • UDP:1194 PORT:0.0.0.0/0(openvpnの通信に利用、全てのIPからのアクセスを許可、特定のIPのみからしVPNを利用しないのであればIPの絞り込み可能。)
  • TCP:22 PORT:自分のIPアドレス(自分のIPからのアクセスを許可、VPNサーバの作成後は削除可能)

を設定してください。

ElasticIPの用意

VPNサーバのグローバルIPアドレスを固定するために、AWSコンソールからElasticIPをVPNサーバにアタッチします。 ElasticIPをVPNサーバに紐付けなくてもVPNサーバは構築出来るのですが、サーバを再起動する度にIPアドレスが変わると、VPN利用者に新しいIPを共有し直す、もしくはDNSレコードの設定を変える必要があるので、後々の運用を考えて最初から固定IPにしたほうが楽になります。

VPNサーバの構築

Amazon Linux 2でVPNサーバを構築します。Amazon Linux 2の最新版のAMIを利用します。 10人以下の利用であれば、VPNサーバの負荷は小さい場合がほとんどなので、インスタンスタイプは低いものでも大丈夫です。今回は、t3.microで構築をしました。

サーバの基本設定

Amazon Linuxを起動したら、下準備として以下の設定をします。

日本時間の設定

sudo timedatectl set-timezone Asia/Tokyo

IPパケット転送の有効化

Linuxはデフォルトではパケットの転送を許可していないので、Linuxサーバ上のあるネットワークインターフェースから別のネットワークインターフェースに向けてIPパケットの転送を有効化するために、以下のカーネルパラメータ設定を行います。

sudo sh -c "echo -e '\nnet.ipv4.ip_forward = 1' >> /etc/sysctl.conf"
# load /etc/sysctl.conf settings
sudo sysctl -p

www.ducea.com serverfault.com

iptablesによるマスカレードの有効化

iptablesで、natテーブルのPOSTROUTINGチェインに対して、VPNの仮想ネットワークIPレンジ(10.8.0.0/24)から、eth0インターフェースに対して来たパケットを、マスカレードするように設定追加します。サーバの再起動後もこのiptables設定が有効となるように、iptables-restore < /etc/sysconfig/iptables の文字列を /etc/rc.localのファイルに追加します。

sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
sudo iptables-save | sudo tee /etc/sysconfig/iptables
sudo chmod +x /etc/rc.d/rc.local
echo 'iptables-restore < /etc/sysconfig/iptables' | sudo tee -a /etc/rc.local

認証局(Certificate Authority)の構築

今回作るOpenVPNのサーバでは、インスタンスの稼働台数が減らせるので、認証局(CAサーバ)の機能とVPNの機能を同じEC2インスタンスに構築します。 本格的に運用するのであれば、CA(Certificate Authority)サーバとOpenVPNサーバは分離させた方がセキュリティ的にはより安全になります。

分離させておくことで、

  • OpenVPNサーバの台数を増やして冗長化させたいときに、認証局周りの設定をOpenVPNサーバを増やした時にどうするか考える手間を減らせます。
  • 認証局サーバを外部からアクセス出来ないように分離しておくことで、認証局秘密鍵(ca.key)が漏洩するリスクを減らすことが出来ます。

Amazon Linux 2ではインストールされるeasy-rsaのパッケージバージョンが上がっており、以前の記事で書いたAmazon Linuxの場合とは実行するコマンドが変わっています。

easy-rsaのインストール

# epelレポジトリからRSA鍵を管理するeasy-rsaパッケージをインストール
sudo amazon-linux-extras install epel
sudo yum install easy-rsa

認証局の作成

# PKI(public key infrastructure)のディレクトリ(pki)階層を作成
sudo /usr/share/easy-rsa/3/easyrsa init-pki

# 認証局の秘密鍵・証明書を作成する、今回はnopassを指定して秘密鍵にはパスフレーズを設定しない、セキュリティ面では設定をしたほうがいい
sudo /usr/share/easy-rsa/3/easyrsa build-ca nopass
## ex. Common Name (eg: your user, host, or server name) [Easy-RSA CA]:toripiyo CA

# Diffe-Hellman パラメータを生成(実行が終了するまで数分かかる)
sudo /usr/share/easy-rsa/3/easyrsa gen-dh

# CRL(証明書執行リスト)を作成
sudo /usr/share/easy-rsa/3/easyrsa gen-crl

VPNサーバの証明書作成

VPNサーバの証明書と秘密鍵の作成

sudo /usr/share/easy-rsa/3/easyrsa build-server-full server nopass

クライアントの証明書作成

OpenVPNサーバを利用するクライアントの証明書作成をします。

# 証明書の期限を365日に設定してtorippyという名前でクライアント証明書と秘密鍵を作成
# 今回の場合は、nopassを指定して秘密鍵にパスフレーズを設定していないが、クライアント証明書が漏洩した時に利用されにくいようにパスフレーズは設定しておいたほうが良い
sudo /usr/share/easy-rsa/3/easyrsa --days=365 build-client-full torippy nopass

# pki ディレクトリをホームディレクトリの配下から/usr/share/easy-rsa/3の配下に移動させる
sudo mv ~/pki /usr/share/easy-rsa/3

OpenVPNサーバの構築

"認証局(Certificate Authority)の構築"で、OpenVPNを利用するのに必要なCA, サーバ, クライアントの秘密鍵・証明書を作成しました。今度は、OpenVPNサーバの設定をします。

OpenVPNのインストール

sudo yum install openvpn

ta.keyの作成

cd /etc/openvpn
sudo openvpn --genkey --secret ta.key

crl.pemを/etc/openvpnに配置

sudo cp /usr/share/easy-rsa/3/pki/crl.pem /etc/openvpn
sudo chmod +r /etc/openvpn/crl.pem

OpenVPNの設定

以下の内容で、設定ファイルを配置します。

sudo vi /etc/openvpn/server.conf
=====================================================
port 1194
proto udp
dev tun # use virtul point-to-point IP link device
user nobody
group nobody
ca /usr/share/easy-rsa/3/pki/ca.crt # CA certificate
cert /usr/share/easy-rsa/3/pki/issued/server.crt # OpenVPN server certificate
key /usr/share/easy-rsa/3/pki/private/server.key # OpenVPN private key
dh /usr/share/easy-rsa/3/pki/dh.pem # DH parameters file
crl-verify /etc/openvpn/crl.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt # keep same IP address
# push "redirect-gateway def1" # all traffic goes through vpn server
# push "dhcp-option DNS 172.31.0.2" # use internal DNS resolver
push "route 172.31.0.0 255.255.0.0" # vpc private ip range
keepalive 10 120
cipher AES-256-GCM
max-clients 20
persist-key
persist-tun
tls-crypt /etc/openvpn/ta.key
status openvpn-status.log
log-append  /var/log/openvpn.log
verb 3
=================================================================

server.confの設定ファイルの内容に関する、いくつかの項目を説明します。

  • dev tun: tun仮想ネットワークデバイスを設定する。tunは、IPv4/IPv6OSIレイヤー3で動作してルーターのように働く。vpnのクライアントは、サーバ側のサブネット(10.1.0.0/255.255.0.0)には参加しないで、vpn独自のサブネット(10.8.0.0/255.255.255.0)からipを割り当てられる。dev tapの場合は、サブネットを分けなくても済むようになるが、サブネットが分かれていないのでクライアントからサーバへのアクセスのIP制限を設定したい時にはtunよりもやりにくくなる。
  • user nobody, group nobody: openvpnは、初期化後はroot権限がなくても動作するようにデザインされているので、nobody ユーザ・グループ権限で動かすようにしている。
  • dh: diffie hellmanパラメータの値をもつファイル。
  • server: VPNネットワークのサブネットを設定する。クライアントはserverで指定しているIPレンジからVPN接続用のIPを割り当てられる。
  • push "redirect-gateway def1": このディレクティブを有効化させると、openvpnに接続するクライアントのデフォルトゲートウェイVPNを経由するように設定できる。つまり、ウェブサイトの閲覧時に全てのトラフィックVPN経由に設定できるようになる、ただし、VPNサーバ側ではNATの設定などをしておかないと正しく動作しない可能性がある。
  • push "dhcp-option DNS": VPNに接続した際に利用するDNSサーバを指定する。AWSの内部DNSサーバのIPを指定すれば、内部DNSレコードを引くようにすることもできる。
  • push "route": VPNの先にあるネットワークと通信したい時にそのネットワークのIPレンジを指定。上の設定例では、AWS内部のVPCネットワーク内のインスタンスとクライアントが通信出来るように、VPCのIPレンジの10.1.0.0を指定している。
  • tls-crypt: あまり深くは把握してないのですが、この鍵情報をサーバとクライアントで事前に共有しておくことで、TLS通信をする際にclient helloの段階から暗号化出来るそうです。暗号化することで、openvpnプロトコルの通信をしていることを検知出来ないようにしたり、TLS DOS攻撃の抑制を出来るようになるそうです。serverfault.com

OpenVPNの起動と自動起動の有効化

sudo systemctl start openvpn@server
sudo systemctl status openvpn@server
sudo systemctl enable openvpn@server

サーバ再起動

サーバを再起動してもopenvpnは正常に稼働するかどうか確認するためにサーバを再起動

sudo reboot

これで、OpenVPNサーバが起動出来ました。次は、クライアントに配布するOVPNファイルをopenvpnサーバ上で作成します。

OVPNファイルの作成

OpenVPNサーバにVPN接続するために、VPNを利用するクライアントは、以下の情報が必要になります。

  • OpenVPNサーバに接続するための設定情報
  • OpenVPNサーバの証明書情報
  • クライアントの証明書
  • クライアントの秘密鍵
  • OpenVPNサーバのStatic Key

これらの情報をそれぞれ別ファイルで作成すると、VPNを利用するクライアントは合計5個のファイルを管理する必要があります。これでは、管理が大変なので、ovpnという拡張子を持つ1個のファイルにまとめます。

ovpnファイルは、以下の手順で作成できます。この例では、torippyというクライアント名のovpnファイルを作成しています。

client_name="torippy"
vpn_server_ip="vpn serverのグローバルip"

cat > ${client_name}.ovpn << EOF
client
dev tun
proto udp
remote ${vpn_server_ip}
port 1194
cipher AES-256-GCM
resolv-retry infinite
nobind
persist-key
persist-tun
ca [inline]
cert [inline]
key [inline]
tls-crypt [inline]
verb 1
keepalive 10 120
remote-cert-tls server
<ca>
`sudo cat /usr/share/easy-rsa/3/pki/ca.crt`
</ca>
<cert>
`sudo cat /usr/share/easy-rsa/3/pki/issued/${client_name}.crt`
</cert>
<key>
`sudo cat /usr/share/easy-rsa/3/pki/private/${client_name}.key`
</key>
<tls-crypt>
`sudo cat /etc/openvpn/ta.key`
</tls-crypt>
EOF

作成したovpnファイルを、openvpnを利用するクライアントユーザに安全な方法を経由して渡します。このovpnファイルが第3者に渡ってしまうとその第3者によってVPNを利用されてしまうので漏れないように気をつける必要があります。万一漏洩しても被害を少なく出来るように、クライアントの証明書作成の際にパスフレーズを設定しておくと、パスフレーズを正しく入力しないとVPNサーバに接続出来ないので、設定しておくほうがセキュリティ的には安心です。

VPNを利用するユーザは、渡されたovpnファイルを利用してVPNに接続します。MacでTunnelblickを利用する場合は、以下の手順となります。

  • tunnelbrickをインストール brew cask install bunnelbrick
  • tunnelbrickを起動する
  • ovpnファイルを画面右上のtunnelbrickのアイコンにドラッグアンドドロップする
  • 表示されてきたプロンプト画面でOnly Meをクリックする
  • Macのパスワード入力を求められるので入力する。証明書のパスフレーズも求められるので入力する。これでVPNサーバとクライアントでコネクションを貼れるようになる。

運用手順

新しいクライアントの追加

  • minckyというクライアントを追加して、そのクライアントのovpnファイルを作成する。(秘密鍵パスフレーズを設定する場合は、nopassのオプションを外す)
  • minckyのovpnファイルの作成方法は、torippyの場合と同様
client_name='mincky'

cd /usr/share/easy-rsa/3
sudo /usr/share/easy-rsa/3/easyrsa --days=365 build-client-full ${client_name} nopass

既存クライアントの証明書失効

  • 退職などに伴って、発行済の証明書からVPNサーバに接続出来なくなるようにする。
client_name='mincky'

cd /usr/share/easy-rsa/3
sudo /usr/share/easy-rsa/3/easyrsa revoke ${client_name}

# /etc/openvpn/crl.pem を更新する
sudo /usr/share/easy-rsa/3/easyrsa gen-crl
sudo cp /usr/share/easy-rsa/3/pki/crl.pem /etc/openvpn

# このコマンドは、すでに上書きするcrl.pemファイルに読み込み権限があれば不要
# nobodyユーザから読み込める権限であれば、openvpnの再起動は不要
# クライアント接続時に、openvpnは、crl.pemファイルを読み込んで失効している証明書リストを確認
sudo chmod +r /etc/openvpn/crl.pem