NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

ACM で発行した証明書を EC2 (Amazon Linux 2023 + Nginx) で利用する

こんにちは、インフラエンジニアのさかもとです。

通常、ACM で発行したパブリック証明書*1を利用するには EC2 の前段に ELB あるいは CloudFront ディストリビューションを構築しそれらに適用する必要があります。
しかし、AWS Certificate Manager (ACM) for Nitro Enclaves を利用すれば ACM で発行したパブリック/プライベート証明書を EC2 で利用することができます。

docs.aws.amazon.com

ACM for Nitro Enclaves は AWS Nitro Enclaves に対応したインスタンスしかサポートされませんが、可用性をあまり求められないシステムなどで TLS を終端するためだけに ELB、CloudFront を構築する必要がないのは嬉しいですよね。

今回 Amazon Linux 2023 + Nginx 構成において ACM for Nitro Enclaves を利用する際にハマった点があったので、導入手順を紹介したいと思います。参考になれば幸いです。

前提条件

ACM for Nitro Enclaves を利用するには以下の条件*2があります。(2024年3月11日時点)

  • RSA 証明書のみ
  • AWS Nitro Enclaves をサポート(※)する Linux インスタンスのみ
  • Asia Pacific (Osaka) and Asia Pacific (Jakarta) はサポート外

※ AWS Nitro Enclaves をサポートするインスタンスは以下を参照ください。
https://docs.aws.amazon.com/ja_jp/enclaves/latest/user/nitro-enclave.html#nitro-enclave-reqs

導入手順

基本は以下 AWS 公式ドキュメントを参考に実施していきます。
docs.aws.amazon.com

ACM 証明書の作成

ACM 証明書の作成手順は割愛いたします。以下を参照してください。
docs.aws.amazon.com なお、EC2 を構築するリージョンと同じリージョンに作成する必要があるのでご留意ください。

IAM Policy および IAM Role の作成

構築する EC2 に以下操作を許可するための IAM Policy を作成します。

  • ACM 証明書に IAM Role を紐付ける権限
  • IAM Role にインラインポリシーを登録する権限

{region} および {account_id} は自身の環境のものに置き換えてください。

{
    "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",
            "Resource": "arn:aws:iam::{account_id}:role/*",
            "Effect": "Allow"
        }
    ]
}

別途 IAM Role を作成し、上記 IAM Policy をアタッチしてください。

なお、こちらの IAM Policy は一連の作業が終われば不要となりますので後ほどデタッチ、削除します。

EIP の払い出し、および DNS 登録

グローバル IP を固定化するため Elastic IP アドレス (EIP) を払い出します。
また ACM 証明書を取得したドメイン名で名前解決ができるよう DNS A レコードの設定をします。
手順については割愛いたします。

EC2 の作成

EC2 は前提条件を満たすインスタンスタイプを選択し、[高度な詳細] から Nitro Enclaves*3 の有効化、また IAM Role の割り当てをします。
SecurityGroup は セットアップを行うための SSH、および外部から動作確認をするための HTTPS を開放しておきます。

項目 内容 備考
AMI Amazon Linux 2023 (2023.3.20240304.0 arm64 HVM kernel-6.1) C6g インスタンスを利用するためアーキテクチャは Arm を選択
インスタンスタイプ c6g.large 2vCPU, 4 GiB メモリ
VPC/サブネット ※パブリックサブネットを選択
SecurityGroup ※ セットアップを行う環境からの SSH および動作確認用の HTTPS を開放したもの
IAM インスタンスプロフィール ※作成した IAM Role [高度な詳細] で設定
Nitro Enclave 有効化 [高度な詳細] で設定

※ 構築する際にインスタンスタイプは前提条件を満たすもので(おそらく)一番料金の安い c6g.large を選択しましたが、vCPU が2コアの場合においてもハマった点がありましたので後ほど紹介します。

EC2 起動後は EIP を割り当ててください。

ACM for Nitro Enclaves と Nginx のインストール

EC2 にログインし、ACM for Nitro Enclaves と Nginx をインストールします。

ACM for Nitro Enclaves のインストール

$ sudo dnf install aws-nitro-enclaves-acm -y

~略~
Installed:
  aws-nitro-enclaves-acm-1.2.0-2.amzn2023.aarch64                aws-nitro-enclaves-cli-1.2.3-0.amzn2023.aarch64                    containerd-1.7.11-1.amzn2023.0.1.aarch64
  docker-25.0.3-1.amzn2023.0.1.aarch64                           iptables-libs-1.8.8-3.amzn2023.0.2.aarch64                         iptables-nft-1.8.8-3.amzn2023.0.2.aarch64
  libcgroup-3.0-1.amzn2023.0.1.aarch64                           libnetfilter_conntrack-1.0.8-2.amzn2023.0.2.aarch64                libnfnetlink-1.0.1-19.amzn2023.0.2.aarch64
  libnftnl-1.2.2-2.amzn2023.0.2.aarch64                          pigz-2.5-1.amzn2023.0.3.aarch64                                    runc-1.1.11-1.amzn2023.0.1.aarch64

Complete!

Nginx のインストール

$ sudo dnf install nginx -y

~略~
Installed:
  generic-logos-httpd-18.0.0-12.amzn2023.0.3.noarch  gperftools-libs-2.9.1-1.amzn2023.0.3.aarch64     libunwind-1.4.0-5.amzn2023.0.2.aarch64        nginx-1:1.24.0-1.amzn2023.0.2.aarch64
  nginx-core-1:1.24.0-1.amzn2023.0.2.aarch64         nginx-filesystem-1:1.24.0-1.amzn2023.0.2.noarch  nginx-mimetypes-2.1.49-3.amzn2023.0.3.noarch

Complete!

ACM 証明書と IAM Role の紐づけ

次に下記コマンドを実行し、利用する ACM 証明書と EC2 に割り当てている IAM Role を紐づけます。
{region}{certificate_ARN}{role_ARN} は自身の環境のものに置き換えてください。

$ aws ec2 --region {region} associate-enclave-certificate-iam-role --certificate-arn {certificate_ARN} --role-arn {role_ARN}

正常にコマンドが通ると以下のような出力が返ってきます。
CertificateS3BucketNameEncryptionKmsKeyId キーの値は次の手順で必要ですのでメモしておいてください。

{
"CertificateS3BucketName": "aws-ec2-enclave-certificate-us-east-1",
"CertificateS3ObjectKey": "arn:aws:iam::123456789012:role/acm-role/arn:aws:acm:us-east-1:123456789012:certificate/d4c3b2a1-e5d0-4d51-95d9-1927fEXAMPLE",
"EncryptionKmsKeyId": "a1b2c3d4-354d-4e51-9190-b12ebEXAMPLE"
}

ACM 証明書へのアクセス、復号化などの権限をロールに付与する

EC2 に割り当てている IAM Role に ACM 証明書へのアクセスと KMS キーで暗号化されたオブジェクトの復号化、自身の IAM Role にアクセスするための権限を付与します。

まず、ポリシーを記載した json ファイルを用意します。

  • {CertificateS3BucketName} はメモした CertificateS3BucketName キーの値
  • {region} は EC2 を起動したリージョン
  • {EncryptionKmsKeyId} はメモした EncryptionKmsKeyId キーの値
  • {role_ARN} は EC2 に割り当てている IAM Role の ARN

に置き換えてください。

$ sudo vi acm-role-policies.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
        "s3:GetObject"
        ],
        "Resource": ["arn:aws:s3:::{CertificateS3BucketName}/*"]
    },
    {
        "Sid": "VisualEditor0",
        "Effect": "Allow",
        "Action": [
            "kms:Decrypt"
        ],
        "Resource": "arn:aws:kms:{region}:*:key/{EncryptionKmsKeyId}"
    },
    {
            "Effect": "Allow",
            "Action": "iam:GetRole",
            "Resource": "{role_ARN}"
    }
  ]
}

次に下記コマンドを実行し、IAM Role にインラインポリシーをアタッチします。
{role_name} は EC2 に割り当てている IAM Role に置き換えてください。

$ aws iam put-role-policy --role-name {role_name} --policy-name acm-role-policy --policy-document file://acm-role-policies.json

冒頭で作成した IAM Policy はもう不要ですので IAM Role からデタッチした後に削除してください。

ACM for Nitro Enclaves および Nginx の設定

ACM for Nitro Enclaves を Nginx で利用するための設定を行います。

はじめに ACM for Nitro Enclaves の設定を行います。
Nginx 用の設定ファイルのサンプルがあるのでリネームします。

$ sudo mv /etc/nitro_enclaves/acm.example.yaml /etc/nitro_enclaves/acm.yaml

リネームした設定ファイル /etc/nitro_enclaves/acm.yamlAcm セクションの 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: ""
+        certificate_arn: "arn:aws:acm:us-east-1:123456789012:certificate/d4c3b2a1-e5d0-4d51-95d9-1927fEXAMPLE"

~略~

次に、Nginx の設定ファイル /etc/nginx/nginx.conf に TLS アクセラレーション機能を利用するための ssl_engine ディレクティブの設定を追加し、pkcs11 を指定します。

$ sudo vi /etc/nginx/nginx.conf

~略~

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

+ ssl_engine pkcs11;

~略~

そして /etc/nginx/nginx.conf の下部にある TLS サーバの設定箇所のコメントアウトを外し、以下設定を行います。

  • server_name の設定。ACM 証明書作成時に指定したホスト名あるいはコモンネーム (CN) を設定
  • ssl_certificate, ssl_certificate_key, ssl_ciphers の削除
  • ssl_protocols TLSv1.2; の追加
  • include "/etc/pki/nginx/nginx-acm.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;

        # Set this to the stanza path configured in /etc/nitro_enclaves/acm.yaml
        include "/etc/pki/nginx/nginx-acm.conf";

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }

}

ACM for Nitro Enclaves および Nginx の起動

設定が完了したら、まず ACM for Nitro Enclaves を起動します。

$ sudo systemctl start nitro-enclaves-acm.service
A dependency job for nitro-enclaves-acm.service failed. See 'journalctl -xe' for details.

なんかエラーがでましたね。ログを確認してみます。(ハマりポイント1)

$ journalctl -xe --no-pager

~略~

nitro-enclaves-allocator[30071]: Auto-generating the enclave CPU pool by using the CPU count...
kernel: nitro_enclaves: No CPUs available in CPU pool
kernel: nitro_enclaves: Error in setup CPU pool [rc=-22]
nitro-enclaves-allocator[30071]: Error: Failed to find suitable CPUs for the Nitro Enclaves CPU pool after checking all NUMA nodes.
systemd[1]: nitro-enclaves-allocator.service: Main process exited, code=exited, status=1/FAILURE

~略~

No CPUs available in CPU pool とあります。 エラーコード (rc=-22) のようなものもあるのでググってみると以下がヒットしました。

Error codes - AWS

Insufficient CPUs available in the pool. The number of requested vCPUs is greater than the number of available vCPUs. Either specify a number of vCPUs less than or equal to the configured vCPU pool size, or preallocate the environment resources so that the vCPU pool includes the number of vCPUs that you want to use. For more information, see Installing the Nitro Enclaves CLI on Linux.

要求された vCPU コア数が使用可能な vCPU コア数を超えているようです。 さらに引用元のリンク先にある内容を確認します。

Installing the Nitro Enclaves CLI on Linux - AWS

Nitro Enclaves uses an allocator service to preallocate vCPUs and memory to the enclaves. The amount of vCPUs and memory to preallocate are defined in the allocator service configuration file (/etc/nitro_enclaves/allocator.yaml). By default, the configuration file is set up to preallocate 512 MiB of memory and 2 vCPUs for use by the enclaves. In some cases, you might need to manually update the configuration file to preallocate a different number vCPUs or amount of memory. For example:
・If you launched an AWS Graviton-based instance with 2 vCPUs, you must configure the allocate service to preallocate only 1 vCPU.

vCPU が2コアの場合は1コアしか割り当てができないようです。なんと。

なので、vCPU の割り当てを修正します。

/etc/nitro_enclaves/allocator.yaml および /etc/nitro_enclaves/acm.yaml の設定を変更します。

$ sudo vi /etc/nitro_enclaves/allocator.yaml

~略~

# How many CPUs to reserve for enclaves.
- cpu_count: 2
+ cpu_count: 1

~略~
$ sudo vi /etc/nitro_enclaves/acm.yaml

~略~

# Enclave general configuration
enclave:
  # Number of vCPUs to be assigned to the enclave
-  cpu_count: 2
+  cpu_count: 1

~略~

では、気を取り直して ACM for Nitro Enclaves を起動します。

$ sudo systemctl start nitro-enclaves-acm.service

いけたか?!ステータスを確認してみます。

$ sudo systemctl status nitro-enclaves-acm.service

~略~

systemd[1]: Starting nitro-enclaves-acm.service - Nitro Enclaves ACM Agent...
systemd[1]: Started nitro-enclaves-acm.service - Nitro Enclaves ACM Agent.
p11ne-agent[30553]: |INFO  | Setting up p11-kit config
p11ne-agent[30553]: |INFO  | Restarting vsock proxy
p11ne-agent[30553]: |INFO  | Syncing token nginx-acm-token
p11ne-agent[30553]: |INFO  | Service: nginx | Force_Start: true | Reload: 1000 | Sync: 600
p11ne-agent[30553]: |INFO  | Reloading NGINX configuration.
p11ne-agent[30553]: |INFO  | NGINX is not running. Starting it now.
p11ne-agent[30553]: |ERROR | Unable to reload NGINX: SystemdStartNginxError(Some(1))

Nginx でエラーが出ています。。。(ハマりポイント2)
ログを確認します。

$ journalctl -xe -u nginx --no-pager

~略~

nginx: [emerg] cannot load certificate key "engine:pkcs11:pkcs11:model=p11ne-token;manufacturer=Amazon;token=nginx-acm-token;id=%01;object=acm-key;type=private?pin-value=4caf27fe2c6e9d3ae7b341ed85519096": ENGINE_load_private_key() failed (SSL: error:13000075:engine routines::not initialised)
nginx: configuration file /etc/nginx/nginx.conf test failed
nginx.service: Control process exited, code=exited, status=1/FAILURE

~略~

ググってみると。。。。
AmazonLinux 2023 & Nginx support · Issue #124 · aws/aws-nitro-enclaves-acm · GitHub

I ran into this myself, and after talking to support was informed that it's simply not supported right yet unless you downgrade openssl-pkcs11 to v0.4.11. I achieved it without a downgrade by adding the following dirty hack to openssl.cnf:

openssl-pkcs11 to v0.4.11. にダウングレードする必要がありそうです。

念のため現在インストールされている openssl-pkcs11 のバージョンを確認します。

$ sudo dnf list --installed openssl-pkcs11
Installed Packages
openssl-pkcs11.aarch64                                                    0.4.12-3.amzn2023.0.1

0.4.12 がインストールされていましたので、0.4.11 にダウングレードします。

インストール可能なバージョンを確認します。

$ sudo dnf --showduplicates search openssl-pkcs11
Last metadata expiration check: 2:21:56 ago on Fri Mar  8 02:45:44 2024.
======================================================================================= Name Exactly Matched: openssl-pkcs11 ========================================================================================
openssl-pkcs11-0.4.12-3.amzn2023.0.1.aarch64 : A PKCS#11 engine for use with OpenSSL
openssl-pkcs11-0.4.11-8.amzn2023.0.3.aarch64 : A PKCS#11 engine for use with OpenSSL
openssl-pkcs11-0.4.12-3.amzn2023.0.1.aarch64 : A PKCS#11 engine for use with OpenSSL

openssl-pkcs11-0.4.11-8.amzn2023.0.3.aarch64 をインストールします。

$ sudo dnf install openssl-pkcs11-0.4.11-8.amzn2023.0.3.aarch64 -y

~略~
Running transaction
  Preparing        :                                                                                                                                                                     1/1
  Downgrading      : openssl-pkcs11-0.4.11-8.amzn2023.0.3.aarch64                                                                                                                        1/2
  Cleanup          : openssl-pkcs11-0.4.12-3.amzn2023.0.1.aarch64                                                                                                                        2/2
  Running scriptlet: openssl-pkcs11-0.4.12-3.amzn2023.0.1.aarch64                                                                                                                        2/2
  Verifying        : openssl-pkcs11-0.4.11-8.amzn2023.0.3.aarch64                                                                                                                        1/2
  Verifying        : openssl-pkcs11-0.4.12-3.amzn2023.0.1.aarch64                                                                                                                        2/2

Downgraded:
  openssl-pkcs11-0.4.11-8.amzn2023.0.3.aarch64

Complete!

いけました!再度 Nginx を起動します。

$ sudo systemctl start nginx

Nginx のステータスを確認します。

$ sudo systemctl status nginx

● nginx.service - The nginx HTTP and reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; preset: disabled)
     Active: active (running) since Mon 2024-03-11 06:58:49 UTC; 14s ago
    Process: 32678 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
    Process: 32681 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
    Process: 32724 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
   Main PID: 32725 (nginx)
      Tasks: 2 (limit: 4478)
     Memory: 3.4M
        CPU: 50ms
     CGroup: /system.slice/nginx.service
             tq32725 "nginx: master process /usr/sbin/nginx"
             mq32726 "nginx: worker process"

systemd[1]: Starting nginx.service - The nginx HTTP and reverse proxy server...
nginx[32681]: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx[32681]: nginx: configuration file /etc/nginx/nginx.conf test is successful
systemd[1]: Started nginx.service - The nginx HTTP and reverse proxy server.

いけてそうです!

ACM for Nitro Enclaves を再起動し、

$ sudo systemctl restart nitro-enclaves-acm.service

ステータスを確認します。

$ sudo systemctl status nitro-enclaves-acm.service

● nitro-enclaves-acm.service - Nitro Enclaves ACM Agent
     Loaded: loaded (/usr/lib/systemd/system/nitro-enclaves-acm.service; disabled; preset: disabled)
     Active: active (running) since Mon 2024-03-11 07:00:15 UTC; 1min 47s ago
    Process: 32791 ExecStartPre=/usr/bin/mkdir -p /run/nitro_enclaves/acm (code=exited, status=0/SUCCESS)
   Main PID: 32792 (p11ne-agent)
      Tasks: 5 (limit: 4478)
     Memory: 10.9M
        CPU: 2.825s
     CGroup: /system.slice/nitro-enclaves-acm.service
             tq32792 /usr/bin/p11ne-agent
             mq32795 nitro-cli run-enclave --eif-path /usr/share/nitro_enclaves/p11ne/p11ne.eif --cpu-count 1 --memory 256

systemd[1]: Starting nitro-enclaves-acm.service - Nitro Enclaves ACM Agent...
systemd[1]: Started nitro-enclaves-acm.service - Nitro Enclaves ACM Agent.
p11ne-agent[32792]: |INFO  | Setting up p11-kit config
p11ne-agent[32792]: |INFO  | Restarting vsock proxy
p11ne-agent[32792]: |INFO  | Syncing token nginx-acm-token
p11ne-agent[32792]: |INFO  | Service: nginx | Force_Start: true | Reload: 1000 | Sync: 600
p11ne-agent[32792]: |INFO  | Reloading NGINX configuration.

大丈夫そうですね。

忘れる前に ACM for Nitro Enclaves の自動起動設定を有効化しておきます。

$ sudo systemctl enable nitro-enclaves-acm.service
Created symlink /etc/systemd/system/multi-user.target.wants/nitro-enclaves-acm.service → /usr/lib/systemd/system/nitro-enclaves-acm.service.

※ Nginx は ACM for Nitro Enclaves が起動してくれるので Nginx の自動起動設定は不要です。

ブラウザでアクセスしてみる

ブラウザでアクセスし、証明書エラーなどが出ていないか確認してみます。 大丈夫そうですね、証明書も ACM で発行されたものがきちんと適用されています。ハッピー!!!!

最後に

以上が Amazon Linux 2023 および Nginx 構成で ACM for Nitro Enclaves を利用する手順になります。
ACM for Nitro Enclaves では証明書の更新もシームレスに行ってくれる*4とのことなのでありがたいかぎりです。
ちなみに Amazon Linux 2023 + Apache 構成も試してみましたが、Apache に関してはダウングレードとかすることなく、公式手順通りで導入できましたので vCPU コア数のみ意識していただけたらと思います。

*1:ACM プライベート証明書はエクスポート可能

*2:https://docs.aws.amazon.com/ja_jp/enclaves/latest/user/nitro-enclave-refapp.html#acm-considerations

*3:Nitro Enclaves の設定はインスタンスが停止状態であればあとからでも有効化することができます。

*4:"ACM for Nitro Enclaves works with NGINX servers and Apache HTTP servers running on Amazon EC2 instances to install the certificate and seamlessly replace expiring certificates." : https://docs.aws.amazon.com/ja_jp/enclaves/latest/user/nitro-enclave-refapp.html

執筆者:坂本陽 三度の飯より四度の飯。最近の推し漫画は「夏目アラタの結婚」です。