【AWS】ACM for Nitro EnclavesでEC2インスタンス用の証明書を管理

## 概要
ACM（AWS Certificate Manager）で発行した証明書をEC2インスタンスで使用したい、という場合の選択肢の一つとして、ACM for Nitro Enclavesを使用する導入手順を記載します。

検証用のEC2インスタンスはterraformで構築します。
インスタンスタイプは m6g.large です。
※Nitro Enclavesを使用する最小要件である、「Graviton使用かつvCPU 2つ以上」を満たすため。

## 想定読者
* ACM for Nitro Enclavesを可能な限り小さいインスタンスタイプで試してみたい！という方
* CloudFrontもALBも使わないEC2 1台構成だけど、証明書は無料のACMのやつを使いたい…という欲張りな方

## この記事に書くこと
* 検証用インスタンスを起動する方法（terraform）
* Nitro Enclavesを使ってACM証明書を使用する手順
* 最小要件（vCPU 2つ）で導入する場合の注意事項

## この記事に書かないこと
* ACMで証明書を新規発行する手順
* terraformの使い方

## 手順
まずはこちらをご覧ください。

https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave-refapp.html

上記がこの記事で言いたいことのほぼ全てではありますが、一部最小要件（vCPU 2つ）で動作させる場合のハマりポイントなどがありますので補足しつつ記載していきます。

### Step1: ACM証明書の作成
AWS Certificate Manager を使用して証明書を取得します。
後続の手順で使うので ARN をどこかに控えておいてください。

### Step2: EC2インスタンスの起動
証明書を適用するEC2インスタンスを起動します。

* インスタンスタイプは **m6g.large**
* Nitro Enclavesを **有効化**
* SSH , SessionManager 等で接続が可能

起動したら、必要なパッケージをインストールします。(ここでは Apache を使用)
```shell
$ sudo amazon-linux-extras enable aws-nitro-enclaves-cli
$ sudo yum -y install httpd mod_ssl
$ sudo yum install aws-nitro-enclaves-acm -y
```
手っ取り早く terraform で構築したい方は下記をどうぞ。各種パッケージインストール済みのインスタンスが起動します。
keypair , profile , SSHを開放するセキュリティグループのインバウンドCIDR Blockはご自身の環境に合わせて入力してください。

https://github.com/sota-shimoyama/terraform-nitro_enclaves_acm

### Step3: IAMロールの準備
あとでEC2インスタンスにアタッチするため、`acm-role`という名前で新規のIAMロールを作成します。
信頼ポリシーは以下のようにしてください。
```JSON
{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Effect":"Allow",
         "Principal":{
            "Service":"ec2.amazonaws.com"
         },
         "Action":"sts:AssumeRole"
      }
   ]
}
```
許可ポリシーはまだ何も設定しなくて大丈夫です。

### Step4: IAMロールとACM証明書の関連付け
先ほど作成したIAMロールとStep1で取得したACM証明書を関連付けます。
aws cliで下記のコマンドを実行してください。
```shell
$ aws ec2 --region region associate-enclave-certificate-iam-role --certificate-arn certificate_ARN --role-arn role_ARN
# region には証明書を作成したリージョンを入れてください。
# certificate_ARN　と role_ARN は証明書とIAMロールのARNで置き換えてください。
```

関連付けが成功すると下記のように出力されます。
後続手順で必要なので控えておいてください。
```JSON
{

"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"
}
```

### Step5: IAMロールに許可ポリシーを追加
Step3で作成したIAMロール`acm-role`に、証明書の格納先となるS3Bucketへアクセスできるよう許可ポリシーを追加します。
下記のポリシーの必要箇所を適宜置き換えて設定してください。

| 置換箇所 | 値 |
|:-|:-|
| CertificateS3BucketName  | Step4で出力された値  |
| region  | 利用中のリージョン  |
| EncryptionKmsKeyId  | Step4で出力された値  |
| arn:aws:iam::123456789012:role/acm-role  | IAMロールのARN（アカウントIDだけ自分のものに置換）  |
```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": "arn:aws:iam::123456789012:role/acm-role"
    }
  ]
}
```

### Step6: EC2インスタンスにIAMロールをアタッチ
起動済みのインスタンスにIAMロールをアタッチします。
マネジメントコンソールから操作するか、下記のコマンドを実行してください。
```shell
$ aws iam create-instance-profile --instance-profile-name acm-instance-profile
$ aws iam add-role-to-instance-profile --instance-profile-name acm-instance-profile --role-name acm-role
$ aws ec2 --region region associate-iam-instance-profile --instance-id instance_id --iam-instance-profile Name=acm-instance-profile
# region と instance_id は各自の環境に応じて置き換えてください。
```

### Step7: ACM for Nitro Enclavesを使用するようWebサーバーを設定
EC2インスタンスにSSHでログインし、設定を行なっていきます。

* Nitro Enclavesにはデフォルトの構成ファイルがあるので、リネームします。
```shell
$ sudo mv /etc/nitro_enclaves/acm-httpd.example.yaml /etc/nitro_enclaves/acm.yaml
```

* リネームした構成ファイルを編集します。編集箇所は「cpu_count」と「certificate_arn」の二箇所です。
```yaml:/etc/nitro_enclaves/acm.yaml
------ 一部抜粋 ------
enclave:
  # Number of vCPUs to be assigned to the enclave
  cpu_count: 2 → 1
  # デフォルト値は2ですが、1に変更
  # Memory (in MiB) to be assigned to the enclave
  memory_mib: 256

tokens:
  # A label for this PKCS#11 token
  - label: httpd-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を入力
```

* Apacheの設定ファイルを作成します。ここでは`/etc/httpd/conf.d/httpd-acm.conf`とします。
```conf:/etc/httpd/conf.d/httpd-acm.conf
<VirtualHost *:443>
ServerName www.example.com
SSLEngine on
SSLProtocol -all +TLSv1.2
SSLCertificateKeyFile "/etc/pki/tls/private/localhost.key"
SSLCertificateFile "/etc/pki/tls/certs/localhost.crt"
</VirtualHost>

# ServerName は各自の環境に応じて置き換えてください。
# SSLCertificateKeyFile および SSLCertificateFile はあとで自動的に書き換わります。
```
もし`ssl.conf`を使用したい場合は、`/etc/nitro_enclaves/acm.yaml`の中に設定ファイルのパスを指定する箇所がありますので、そちらを書き換えてください。

* Nitro Enclaves Allocator の構成ファイル `/etc/nitro_enclaves/allocator.yaml` を編集します。編集箇所は「cpu_count」です。
:::note warn
vCPUが2つのインスタンスタイプの場合、この工程を飛ばすと nitro-enclaves-acm.service を正常に起動できません。
:::
```yaml:/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 → 1
# デフォルト値は2なので、1に変更します。
#
# Alternatively, the exact CPUs to be reserved for the enclave can be explicitely
# 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がデフォルトではCPUを2つ使用するようになっており、そのままだと親インスタンス用のCPUが残らないためです。詳しくは下記リンク先の　**Important**　の部分を参照してください。

https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave-cli-install.html

* ACM for Nitro Enclaves serviceを起動、有効化します。
```shell
$ sudo systemctl start nitro-enclaves-acm.service
$ sudo systemctl enable nitro-enclaves-acm
```

## おわりに
お疲れ様でした。
ここまでの設定で、nitro-enclaves-acm.serviceによりACMの証明書を使用するようApacheの設定ファイル内の証明書のパスが書き換わります。

Apacheを再起動してからWebサーバーへアクセスし、ACMの証明書が適用されていることを確認してください。

私がこの記事を書こうと思ったきっかけは、
「ACM for Nitro Enclaves試したいけど、Intel,AMDのインスタンスだと最低4vCPU必要…？よろしい、ならばGravitonだ。」
と思い立ってm6g.largeのインスタンスで検証したところ、cpu_count周りで少しハマったことです。

公式のドキュメントの手順通りやってるのにサービスが起動しない！と悩める人が一人でも減ると幸いです。


##### Nitro Enclavesの要件

https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave.html#nitro-enclave-reqs

* Virtualized Nitro-based instance
  * Intel or AMD-based instances with at least 4 vCPUs, excluding `T3`, `T3a`, and `u-*`
  * AWS Graviton-based instances with at least 2 vCPUs, excluding `A1`, `T4g`, `G5g`, `Im4gn`, and `Is4gen`
* Linux or Windows (2012 R2 or later) operating system