カスタマーサクセス部の山﨑です。
今回はACM for Nitro Enclaves を利用してNginxへのHTTPS通信を実装してみました
今回の構成
- ALBの代わりにNginxをインストールしたEC2インスタンスをリバースプロキシとして利用する
- バックエンドのEC2インスタンスにはApacheをインストールしてWebサーバーとして利用する
- クライアントからリバースプロキシまでの通信は、ACM for Nitro Enclaves を利用したHTTPS通信を利用する
- 各EC2インスタンスはAmazon Linux 2を利用する(AWS CLI v2 をインストールしておく)
AWS Nitro Enclaves について
AWS Nitro Enclaves はEC2インスタンスという仮想マシン内に、新しく仮想マシン(Enclave)を作成する技術です。EnclaveはEC2インスタンス(親インスタンス)とは異なるカーネル/メモリ/vCPU を持っています。
EnclaveにはVsockと呼ばれるUNIXソケットのような内部通信用のソケットを使うことでのみ通信可能で、外部環境と分離されているため安全性が高いです。仕組みとしては、EC2インスタンス内でDockerを起動してEnclaveのイメージファイルをビルドし、「nitro-cli run-enclave」で仮想マシン(Enclave)を作成します。「virsh」コマンドで仮想マシンを起動させているようなイメージです。
Nitro Enclaves には利用可能なインスタンスタイプ等の制限があるため、ご利用の際は以下の公式ドキュメントをご確認ください。
https://docs.aws.amazon.com/ja_jp/enclaves/latest/user/nitro-enclave.html#nitro-enclave-reqs
AWS Certicate Manager について
AWS Certificate Manager(ACM)とは、SSL/TLS(Secure Sockets Layer/Transport Layer Security)証明書のプロビジョニング(準備)、管理、デプロイを一元管理することができるマネージドサービスです。
以下、ACMの特徴を列挙します。特に証明書が自動更新される点は更新管理の手間が省けるため助かります。
- 無償でSSL/TLS証明書(DV証明書)を発行することができる
- ELB等のAWSサービスと統合されているため証明書の配置が容易にできる
- SSL/TLS証明書(DV証明書)は自動更新される
- ACMで発行していないSSL/TLS証明書も管理することができる
ACM for Nitro Enclaves について
ACM for Nitro Enclavesは、EC2インスタンス上のWebアプリケーションやWebサーバーでACMで管理されているSSL/TLS証明書を安全に利用するためのサービスです。このサービスを使用すると、証明書をEC2インスタンスのEnclave内に安全に保管してWebサーバーで直接利用することができます。
これにより、証明書のプライベートキーが外部に漏れる心配がなく、証明書の購入や更新といった管理もACMによって自動化されます。
実装してみる
事前作業
以降は「ACM for Nitro Enclaves」と「リバースプロキシ(Nginx)」の部分のみ触れていきますので、以下の作業は事前に完了させています。
- 独自ドメインの取得
- ACMを用いたパブリック証明書の発行
- Route53のパブリックホストゾーンでリバースプロキシのPublic IPアドレスをAレコードとして登録
- Apache HTTPサーバーの構築とHTMLファイルの配置
リバースプロキシ(Nginx)
基本的にはAWS公式ドキュメントの手順通りに進めていきます
Nitro Enclaves CLI のインストール
$ sudo amazon-linux-extras install aws-nitro-enclaves-cli -y $ sudo yum install aws-nitro-enclaves-cli-devel -y $ sudo usermod -aG ne username $ sudo usermod -aG docker username $ nitro-cli --version
Nitro Enclaves CLI のインストールが完了すると、「/etc/nitro_enclaves/allocator.yaml」というファイルが作成されています。これは仮想マシンとしてのEnclaveに割り当てるCPUとメモリを定義した設定ファイルです。
$ cat /etc/nitro_enclaves/allocator.yaml --- # Enclave configuration file. # # How much memory to allocate for enclaves (in MiB). memory_mib: 512 # # How many CPUs to reserve for enclaves. cpu_count: 2 # # Alternatively, the exact CPUs to be reserved for the enclave can be explicitly # configured by using `cpu_pool` (like below), instead of `cpu_count`. # Note: cpu_count and cpu_pool conflict with each other. Only use exactly one of them. # Example of reserving CPUs 2, 3, and 6 through 9: # cpu_pool: 2,3,6-9
Nitro Enclaves は「nitro-enclaves-allocator.service」を使用して「/etc/nitro_enclaves/allocator.yaml」に設定されたvCPU とメモリをEnclaveに事前に割り当てます。そのため、「nitro-enclaves-allocator.service」を自動起動させ、インスタンスが起動するたびにEnclaveにリソースが割り当てられるようにしておきます。
また、Enclaveを起動する際のイメージファイルのビルドにはDockerを利用するため、Dockerも自動起動させておきます。
$ sudo systemctl enable --now nitro-enclaves-allocator.service $ sudo systemctl enable --now docker
IAM Instance Profile の作成とリバースプロキシへの関連付け
以下のIAMロールを作成し、IAM Instance Profile としてリバースプロキシに関連付けます。
信頼ポリシー
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
IAMポリシー
「ec2:AssociateEnclaveCertificateIamRole」はIAMロールをEnclaves内で使用されるACM証明書に関連付けるためのポリシーです。
{ "Version": "2012-10-17", "Statement": [ { "Action": "ec2:AssociateEnclaveCertificateIamRole", "Resource": [ "arn:aws:acm:{region}:{account_id}:certificate/*", "arn:aws:iam::{account_id}:role/*" ], "Effect": "Allow" }, { "Action": "iam:PutRolePolicy", // IAM Instance Profile にインラインポリシーを追加しないのであれば不要 "Resource": "arn:aws:iam::{account_id}:role/*", "Effect": "Allow" } ] }
IAMロールをEnclaves内で使用されるACM証明書に関連付け
以下のコマンドを実行し、IAMロールをEnclaves内で使用されるACM証明書に関連付けます。
$ aws ec2 --region region associate-enclave-certificate-iam-role --certificate-arn certificate_ARN --role-arn role_ARN
関連付けが完了すると以下のようなレスポンスが返ってきます。
{ "CertificateS3BucketName": "aws-ec2-enclave-certificate-ap-northeast-1", "CertificateS3ObjectKey": "arn:aws:iam::123456789012:role/acm-role/arn:aws:acm:ap-northeast-1:123456789012:certificate/d4c3b2a1-e5d0-4d51-95d9-1927fEXAMPLE", "EncryptionKmsKeyId": "a1b2c3d4-354d-4e51-9190-b12ebEXAMPLE" }
上記より2つのことが分かります。
- S3バケット(aws-ec2-enclave-certificate-ap-northeast-1)に証明書が配置されている
- 証明書(S3オブジェクト)はKMSで暗号化されている
つまり、リバースプロキシがACM証明書を利用するためにはS3バケットから証明書をダウンロード(s3:GetObject)且つ復号化(kms:Decrypt)する必要があるということです。
IAM Instance Profile にポリシーを追加する
IAM Instance Profile として関連付けているIAMロールに以下のIAMポリシーを追加します。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": ["arn:aws:s3:::ACM証明書への関連付けで応答があったCertificateS3BucketName/*"] }, { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": "arn:aws:kms:region:*:key/ACM証明書への関連付けで応答があったEncryptionKmsKeyId" }, { "Effect": "Allow", "Action": "iam:GetRole", "Resource": "IAM Instance Profile のロールARN" } ] }
ACM for Nitro Enclaves の設定
「aws-nitro-enclaves-acm」をインストール後、Nginx 用の設定サンプルがあるのでリネームしておきます。
$ sudo yum install aws-nitro-enclaves-acm -y $ sudo mv /etc/nitro_enclaves/acm.example.yaml /etc/nitro_enclaves/acm.yaml
リネームした設定ファイルの「Acm セクション」にある「certificate_arn」に利用する ACM 証明書の ARN を追記します。
$ sudo vi /etc/nitro_enclaves/acm.yaml ... tokens: # A label for this PKCS#11 token - label: nginx-acm-token # Configure a managed token, sourced from an ACM certificate. source: Acm: # The certificate ARN # Note: this certificate must have been associated with the # IAM role assigned to the instance on which ACM for # Nitro Enclaves is run. certificate_arn: "ACM証明書のARN" ...
Nginxの設定
「/etc/nginx/nginx.conf」を以下のように変更しておきます。
- 「ssl_engine pkcs11;」を追記
$ cat /etc/nginx/nginx.conf # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; ssl_engine pkcs11; # 追記箇所 ...(省略)
- TLS通信用のserverディレクティブに「locationディレクティブ」「TLS通信の設定」を追記
$ cat /etc/nginx/nginx.conf ... # Settings for a TLS enabled server. # server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example.com; root /usr/share/nginx/html; ssl_protocols TLSv1.2; # 追記箇所 ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_prefer_server_ciphers on; location /services/ { # 追記箇所 proxy_pass http://xx.xx.xx.xx/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /recruit/ { # 追記箇所 proxy_pass http://xx.xx.xx.xx/; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # Set this to the stanza path configured in /etc/nitro_enclaves/acm.yaml include "/etc/pki/nginx/nginx-acm.conf"; # 追記箇所 } }
最後に「nitro-enclaves-acm.service」を開始し、インスタンス起動時に自動的に開始するようにします。
$ sudo systemctl start nitro-enclaves-acm.service
$ sudo systemctl enable nitro-enclaves-acm
動作確認
Route53に登録しておいた独自ドメイン(リバースプロキシ)宛にWebブラウザからリクエストを送信すると、バックエンドのApacheにプロキシされて、無事にHTTPS通信ができました!
まとめ
ということで、今回はACM for Nitro Enclaves を利用してNginxへのHTTPS通信を実装してみました。ACM証明書なので、証明書が自動更新されるのは嬉しいポイントですね。
山﨑 翔平 (Shohei Yamasaki) 記事一覧はコチラ
カスタマーサクセス部所属。2019年12月にインフラ未経験で入社し、AWSエンジニアとしてのキャリアを始める。2023 Japan AWS Ambassadors/2023-2024 Japan AWS Top Engineers