内部通信暗号化が要件になった場合のAWSでの実現案

## 0. はじめに
IPAから2023/7/18付けで[重要情報を扱うシステムの要求策定ガイド Ver.1.0](https://www.ipa.go.jp/digital/kaihatsu/system-youkyu.html)という資料が公開されました。この中に「システム基盤内通信路でのデータ暗号化の確保」という要求項目があります。
公共分野の通常のWebシステムではユーザ→ロードバランサまでの通信はHTTPSで暗号化しますが、ロードバランサより内部のアプリケーションサーバ、DBサーバなどとの通信は暗号化しないことが多いです。しかし、今後この資料を元に重要な情報を扱う公共システムで内部通信暗号化が必須要件とされてくる可能性があります。
そこで内部通信暗号化をAWSで行う方法について考え、1つの案について設計への影響を検証してみました。

## 目次
[0. はじめに](#0-はじめに)
[1. 暗号化する箇所](#1-暗号化する箇所)
[2. それぞれ暗号化する方法](#2-それぞれ暗号化する方法)
[3. ミドルウェアによらず透過的に暗号化する方法](#3-ミドルウェアによらず透過的に暗号化する方法)
[4. WireGuardでの接続検証](#4-wireguardでの接続検証)
[5. WireGuard利用による性能への影響](#5-wireguard利用による性能への影響)
[6. まとめ](#6-まとめ)

## 1. 暗号化する箇所
ガイドでは内部通信暗号化について「2.2 自律性確保のための要求項目一覧（データ）」に次のように書かれています。少なくとも重要なデータが通る内部通信は運用サーバも含めてすべて暗号化するよう読み取れます。

|No.|データの機密性・完全性・<br>可用性確保のための対策|対策の目的|対策の詳細内容（要求項目）|
|:--|:--|:--|:--|
|A-13|システム基盤内通信路でのデータ暗号化の確保|外部からシステム基盤内へ侵入された場合や内部の運用者が不正を行った場合においても、ネットワークタッピングなどによるデータの漏洩を防止する。|• 重要なデータを扱うアプリケーションについて、システム基盤内の複数の機器間における全ての通信路上の通信が暗号化されること。|

一般的なWebシステムで暗号化するべき箇所としては最低限以下が考えられます。
■外部通信
ユーザ～ロードバランサ

■内部通信
1. ロードバランサ～Webサーバ
2. Webサーバー～APサーバ
3. APサーバ～DBサーバ
4. 各サーバ・ネットワーク機器～運用監視サーバ
5. 各サーバ～ストレージ

## 2. それぞれ暗号化する方法
### 2-1. ロードバランサ～Webサーバ
ロードバランサ～Webサーバの通信暗号化はいくつかパターンはありますがいずれにしてもWebサーバがHTTPSで通信するためにSSL対応する必要があります。
- ALBを使いターゲットグループとの通信プロトコルをHTTPSにする
- NLBを使う
- ロードバランサを使わない

### 2-2. Webサーバー～APサーバ
例えばAPサーバにJBossを使っている場合はJBossでSSLを有効化することで暗号化できます。
[JBoss EAP 6 Web サーバーでの SSL 暗号化の実装](https://access.redhat.com/documentation/ja-jp/jboss_enterprise_application_platform/6.2/html/security_guide/implement_ssl_encryption_for_the_jboss_enterprise_application_platform_web_server1)

### 2-3. APサーバ～DBサーバ
DBサーバにOracleを使っている場合はOracle独自のNNE(ネイティブネットワーク暗号化)で暗号化するかSSLで暗号化する方法が可能です。RDSのユーザガイドでも紹介されています。
[Oracle ネイティブネットワーク暗号化](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/Appendix.Oracle.Options.NetworkEncryption.html)
[Oracle Secure Sockets Layer](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/Appendix.Oracle.Options.SSL.html)

DBサーバにPostgreSQLを使っている場合はSSLで暗号化が可能です。
[PostgreSQL DB インスタンスで SSL を使用する](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/PostgreSQL.Concepts.General.SSL.html)

### 2-4. 各サーバ・ネットワーク機器～運用監視サーバ
監視にCloudWatch Agentを使う場合、HTTPS通信が使われているため暗号化されています。
[メトリクスとログを CloudWatch に送信するためにインターネットにアクセスできない Auto Scaling グループで EC2 インスタンスを設定する方法を教えてください。](https://repost.aws/ja/knowledge-center/cloudwatch-send-logs-without-internet)

もしHinemosなどのミドルウェアを使う場合、SNMPv3を使って暗号化することができます。
[Hinemos ver.6.0 ミッションクリティカル機能のご紹介](https://www.hinemos.info/themes/custom/hinemos/pdf/missioncritical_20170418.pdf)

### 2-5. 各サーバ～ストレージ
各サーバ～ストレージの通信は通信する前に保存するデータ自体を暗号化しておけば良いので、OS上でディスク暗号化するといったデータ暗号化で対応するのが良いと思います。
S3を使う場合は2023年1月5日から、Amazon S3へのすべての新しいオブジェクトのアップロードは、追加コストなしで自動的に暗号化されています。
[サーバー側の暗号化によるデータの保護](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/serv-side-encryption.html)

S3通信の暗号化を強制するにはバケットポリシーのaws:SecureTransport でS3へのリクエストをHTTPSのみ許可することができます。
[どの S3 バケットポリシーを使用すれば、AWS Config ルール s3-bucket-ssl-requests-only に準拠することができますか?](https://repost.aws/ja/knowledge-center/s3-bucket-policy-for-config-rule)

## 3. ミドルウェアによらず透過的に暗号化する方法
Webサーバー～APサーバ、APサーバ～DBサーバのようなサーバ間の通信暗号化では使うミドルウェアごとに設定が必要ですが、ミドルウェアによらない透過的な暗号化方式も考えられます。
透過的な暗号化方式としてAWSではNitro SystemのEC2インスタンスの場合にはハードウェアレイヤで自動的に暗号化されます。
[AWS Nitro System による転送中のデータの暗号化](https://docs.aws.amazon.com/ja_jp/fsx/latest/ONTAPGuide/nitro-encryption.html)
しかし、NitroでないインスタンスのEC2間で暗号化する場合など別の方法も考える必要があるため、大きく以下の3つを考えてみました。
1. MACSecによる暗号化
2. サーバ間でSSHポートフォワードによる暗号化
3. サーバ間でVPN接続による暗号化

1のMACSecはネットワークレイヤ2レベルで暗号化する技術でDirectConnectでは提供されているのですが、検証したところEC2サーバ間では接続できませんでした。レイヤ2レベルで動作するARPも含めて通信方式が変わるため、AWS内のネットワークが対応していないと思われます。

2のSSHポートフォワードについては問題なく接続できました。ただ、通信プロトコルがTCPである必要があり、接続したいポートごとに設定が必要になってしまうため、複雑な通信要件には対応が難しくなります。

3のVPN接続による暗号化を行った場合どうなるか、以下で検証してみたいと思います。

## 4. WireGuardでの接続検証
VPN接続にはOpenVPNなどいくつかのソフトウェアが使えますが、今回はLinuxカーネルに組み込まれていて高速に動作できるというWireGuardで検証してみました。
WireGuardはUDPを使って高速にVPN接続できるよう作られたソフトウェアで、OpenVPNなどに比べて新しい暗号化方式を採用していること、トンネルが切断されにくく、短時間で再接続できることなどが特徴とされています。
[WireGuard - Wikipedia](https://ja.wikipedia.org/wiki/WireGuard)

構成は以下のようにしました。EC2のインスタンスタイプはすべてt2.microで、OSはAmazon Linux 2を利用しました。
EC2の実際のIPアドレスは172.25.*.*ですが、VPN上で192.168.0.*のIPアドレスを付与します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/e0affba0-a4bd-74d7-f911-9e8792bcdefb.png)


以下のコマンドでWireGuardをインストールします。
```
$ sudo yum update
$ sudo amazon-linux-extras install epel
$ sudo yum update
$ sudo yum install wireguard-tools
```

事前にセキュリティグループなどをEC2間で通信できるよう設定しておき、各サーバで以下のコマンドを実行します。コマンドはこちらのページのものを使わせていただきました。
[WireGuardを利用したVPN構築](https://zenn.dev/motisan/articles/20221230_wireguard)

WireGuardで使用するポートは51820としました。wg set wg0 peerで接続先を設定する際に接続先のEC2で作った公開鍵(SERVERPUBKEY, CLIENTPUBKEY1, CLIENTPUBKEY2)を設定する必要があります。


```172.25.21.252
$ wg genkey > serverkey
$ wg pubkey < serverkey > serverpubkey
$ chmod 0600 server*
$ ip link add dev wg0 type wireguard
$ ip addr add dev wg0 192.168.0.1/24
$ wg set wg0 listen-port 51820 private-key ./serverkey
$ cat serverpubkey #SERVERPUBKEY
$ wg set wg0 peer CLIENTPUBKEY1 persistent-keepalive 25 allowed-ips 192.168.0.2/32 endpoint 172.25.21.23:51820
$ wg set wg0 peer CLIENTPUBKEY2 persistent-keepalive 25 allowed-ips 192.168.0.3/32 endpoint 172.25.22.23:51820
$ ip link set dev wg0 up
```

```172.25.21.23
$ wg genkey > clientkey
$ wg pubkey < clientkey > clientpubkey
$ chmod 0600 client*
$ ip link add dev wg0 type wireguard
$ ip addr add dev wg0 192.168.0.2/24
$ wg set wg0 listen-port 51820 private-key ./clientkey
$ cat clientpubkey #CLIENTPUBKEY1
$ wg set wg0 peer SERVERPUBKEY persistent-keepalive 25 allowed-ips 192.168.0.1/32 endpoint 172.25.21.252:51820
$ ip link set dev wg0 up
```

```172.25.22.23
$ wg genkey > clientkey
$ wg pubkey < clientkey > clientpubkey
$ chmod 0600 client*
$ ip link add dev wg0 type wireguard
$ ip addr add dev wg0 192.168.0.3/24
$ wg set wg0 listen-port 51820 private-key ./clientkey
$ cat clientpubkey #CLIENTPUBKEY2
$ wg set wg0 peer SERVERPUBKEY persistent-keepalive 25 allowed-ips 192.168.0.1/32 endpoint 172.25.21.252:51820
$ ip link set dev wg0 up
```

これでVPN接続ができました。元のIPアドレスで接続すれば通常の通信となり、192.168.0.*で接続すればVPNトンネルを通る暗号化通信になります。この設定では再起動時には自動接続されないので、永続化したい場合はsystemctlで設定します。
[8.8. wg-quick サービスを使用した WireGuard サーバーの設定 Red Hat Customer Portal](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/9/html/configuring_and_managing_networking/proc_configuring-a-wireguard-server-by-using-the-wg-quick-service_assembly_setting-up-a-wireguard-vpn)

同セグメントのEC2間でも別セグメントのEC2間でも同じように接続できました。ただし、allowed-ipsで/32以外を設定してしまうと同セグメント間で自動的に用意されるルーティングなどと競合してしまう場合があるので注意が必要です。/32で設定することでロンゲストマッチによりVPNのルートが優先されるようにしています。
また、WireGuardは設定したポート51820/UDPで通信していますが、トンネル上は自由なポートで通信できます。そのため、セキュリティグループやAWS Network FirewallでEC2間のWireGuard通信を許可してしまうと、トンネル上の通信を制限する方法を別途検討する必要があることにも注意が必要です。

## 5. WireGuard利用による性能への影響
暗号化による性能への影響を確認するため、iperfというツールで帯域幅を確認しつつ、CPU使用率を確認して比較してみました。※iperfはyum installでインストールできます。

通常の通信は934Mbps出ましたが、暗号化通信は273Mbpsになってしまいました。CPU使用率をグラフにしたものが以下です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571868/14f164bd-7de0-a264-e592-c9b61313bbf5.png)

高くなっている部分がiperfを実行した時間ですが、だいたい同じ程度のCPU使用率が同じ時間続いています。暗号化通信では暗号化と復号化の処理の分CPUを使うはずですが、まだ余裕があるCPU使用率を使い切れていません。これは処理にかかる時間の分通信遅延が大きくなっているせいだと考えられます。
そこで、1Mbyte送信あたりのCPU使用率の合計を比較してみると次のようになりました。
- 通常: 1.693
- VPN: 5.705

→VPNは通常の約3.37倍

つまり、暗号化通信では通常の通信に加えて暗号化/復号化にもCPUを使うため通常の3.37倍のCPU使用率となるが、今回の環境では暗号化/復号化処理の分通信遅延が大きくなり、送信できるデータ量が少なくなったということになります。

ただし、今回は違いがわかりやすいよう小さいシングルコアのCPUで検証しましたが、通常サーバに使われる大きいマルチコアのCPUであれば通信に使われるCPUは数%のため、性能差が 3倍になるほどの大きな影響はないものと考えられます。また、試してはいませんがVPNでも3並列で通信すれば合計で同程度の通信速度が出せるはずです。(今回の環境では先にCPU使用率が100%に達してしまいそうですが)

## 6. まとめ
重要な情報を扱う公共システムで今後要件にされる可能性のある内部通信暗号化についてAWS上での対応策と設計への影響を検証しました。
トンネル上の通信を制限する方法(セキュリティ設計)や通信遅延増及びCPU使用率増(性能設計)といった設計への影響は小さくないため、そもそも暗号化が必要な箇所を絞る検討も重要だと思います。

※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。