AWS環境にAmazon Linux 2でOpenVPNサーバを構築・運用する
以前、AWS環境にOpenVPNサーバを構築して運用する記事を書きました。
この記事は、Amazon Linuxでの構築を前提に書かれているのですが、いまはAmazon Linux 2が主流となっています。そこで、Amazon Linuxから、Amazon Linux 2向けに記事を書き直してみました。
目次
どんな方式でVPNサーバを構築するか
以下記事の通り、VPN通信を実現するには様々な方法(PPTP, L2TP/IPsec, SSTP, IKEv2, )があります。
今回も以前の記事と同じように、TLSを利用したOpenVPNの証明書認証方式でVPNサーバを構築します。
VPNの認証方式を証明書認証にすることで、ユーザごと個別に証明書を有効・無効にしてアクセスの許可・拒否を設定出来ます。 パスワード認証の方が設定は楽ですが、退職者が出るたびに新しいパスワードを設定して、VPN利用者全員にパスワードの更新を周知する手間が生じるので、長期的に見れば証明書認証の方が運用は楽になります。
AWSのVPC上にVPNサーバを構築することで、以下のようなケースで利用できるようになります。
- EC2とのSSH通信をVPNサーバ経由でネットワークを閉じて出来るようになるので、SSH通信をしたいサーバにパブリックIPを付与する必要がなくなります。SSHの入り口を外部から無くせるので、外部からの脅威を軽減できます。
- AWS上に構築した社内向けWEBアプリを、プライベートIPのみの閉じたネットワークで公開出来るようになります。パブリックIPを持たないので外部から社内ツールに侵入される脅威を軽減できます。(もちろん、たとえ閉じたネットワークでも認証・権限管理、監査機能をアプリ側にちゃんと実装する方が安心です。)
構築手順
構成図
以下のネットワーク構成を想定して、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
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サーバの証明書作成
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/IPv6のOSIレイヤー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を利用するクライアントは、以下の情報が必要になります。
これらの情報をそれぞれ別ファイルで作成すると、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