implementing-aws-nitro-enclave-security について
AWS Nitro Enclavesで機密データを扱うエンジニア向けの実装ガイドです。環境構築やイメージ作成、アテステーションに基づくKMSポリシー、vsock通信、検証、運用を扱います。
主な機能
- 01Nitro Enclaves環境の構成
- 02EIFイメージのビルドと署名
- 03アテステーション対応KMSポリシー
- 04安全なvsock通信の実装
- 05アテステーション文書の検証
- 06エンクレーブの起動と監視
活用シーン
- 01個人情報や金融データの隔離処理
- 02検証済みイメージへのKMS復号制限
- 03アテステーションによる複数エンクレーブの認証
- 04親インスタンスからエンクレーブへの復号移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill implementing-aws-nitro-enclave-securityインストール後、Claude Code で/implementing-aws-nitro-enclave-securityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
AWS Nitro Enclave のセキュリティを実装する
使用するタイミング
- EC2 インスタンスのオペレーターや管理者から隔離しなければならない機密データ(PII、PHI、金融記録、暗号化シークレット)を処理する場合
- 親インスタンス上の root レベルのアクセスでもエンクレーブのメモリや状態を読み取れない、コンフィデンシャルコンピューティングのパイプラインを構築する場合
- 検証済みの特定のエンクレーブイメージのハッシュに KMS の復号権限を紐付ける、暗号化証明(アテステーション)のワークフローを実装する場合
- 2 つ以上のエンクレーブがデータを交換する前にアテステーションで相互認証する、マルチパーティ計算環境をデプロイする場合
- 現在親インスタンス上でシークレットを復号しているワークロードを、復号処理をエンクレーブの境界内へ移行することで強化する場合
以下の場合は使用しないでください:ワークロードがハードウェアレベルの隔離を必要とする機密データを扱わない場合、インスタンスタイプが Nitro Enclaves をサポートしない場合(少なくとも 4 vCPU の Nitro ベースのインスタンスが必要)、またはレイテンシの制約により vsock 通信のオーバーヘッドが許容できない場合。
前提条件
- Nitro 対応の EC2 インスタンス(m5.xlarge 以上、C5、R5、M6i ファミリー)を起動する権限を持つ AWS アカウント
- 親 EC2 インスタンス上にインストールされた AWS CLI v2 および
nitro-cliツールセット(Amazon Linux 2 または AL2023) - エンクレーブイメージファイル(EIF)をビルドするために親インスタンスにインストールされた Docker
- エンクレーブの IAM ロールにキーポリシーの権限を付与した AWS KMS の対称鍵
- エンクレーブ側の KMS 操作に使用する
aws-nitro-enclaves-sdk-cまたは Python のaws-encryption-sdk /etc/nitro_enclaves/allocator.yamlで十分なメモリと vCPU を割り当てて構成された Nitro Enclaves アロケーターサービス
ワークフロー
ステップ 1:Nitro Enclaves 環境を構成する
エンクレーブの起動をサポートするように親 EC2 インスタンスを設定します。
- Nitro Enclaves CLI をインストールする:Amazon Linux 2 では、ツールとアロケーターをインストールします:
sudo amazon-linux-extras install aws-nitro-enclaves-cli sudo yum install aws-nitro-enclaves-cli-devel -y sudo systemctl enable --now nitro-enclaves-allocator.service sudo systemctl enable --now docker sudo usermod -aG ne ec2-user sudo usermod -aG docker ec2-user - メモリと CPU の割り当てを構成する:
/etc/nitro_enclaves/allocator.yamlを編集して、エンクレーブ用のリソースを予約します。エンクレーブには、親インスタンスから切り出される専用のメモリが必要です:
アロケーターを再起動します:--- memory_mib: 4096 cpu_count: 2sudo systemctl restart nitro-enclaves-allocator.service - セットアップを検証する:
nitro-cli describe-enclavesを実行して、CLI が Nitro ハイパーバイザーと通信できることを確認します。空の JSON 配列[]は、エンクレーブが実行されておらず、セットアップが正しいことを示します。
ステップ 2:エンクレーブイメージファイル(EIF)をビルドする
機密ワークロードを署名済みのエンクレーブイメージにパッケージ化します。
-
アプリケーションの Dockerfile を作成する:エンクレーブは最小限の Linux 環境で動作します。アプリケーションは vsock のみを介して通信します:
FROM amazonlinux:2 RUN yum install -y python3 python3-pip && \ pip3 install boto3 cbor2 cryptography requests COPY enclave_app.py /app/enclave_app.py WORKDIR /app CMD ["python3", "enclave_app.py"] -
nitro-cli で EIF をビルドする:Docker イメージをエンクレーブイメージファイルに変換し、PCR の測定値を取得します:
docker build -t enclave-app:latest . nitro-cli build-enclave \ --docker-uri enclave-app:latest \ --output-file enclave-app.eif出力には、重要な 3 つの PCR 値が含まれます:
- PCR0:エンクレーブイメージファイルの SHA-384 ハッシュ(イメージ全体のダイジェスト)
- PCR1:Linux カーネルとブートストラッププロセスの SHA-384 ハッシュ
- PCR2:アプリケーションコードの SHA-384 ハッシュ これらの値を記録してください。アテステーションに基づくアクセス制御のために KMS キーポリシーで使用されます。
-
署名済み EIF をビルドする(本番環境では推奨):署名証明書を生成し、それを使用して PCR8 を作成します:
openssl ecparam -name secp384r1 -genkey -noout -out enclave_key.pem openssl req -new -key enclave_key.pem -sha384 \ -nodes -subj "/CN=Enclave Signer" -out enclave_csr.pem openssl x509 -req -days 365 -in enclave_csr.pem \ -signkey enclave_key.pem -sha384 -out enclave_cert.pem nitro-cli build-enclave \ --docker-uri enclave-app:latest \ --output-file enclave-app.eif \ --private-key enclave_key.pem \ --signing-certificate enclave_cert.pemPCR8(署名証明書のハッシュ)により、特定の証明書で署名されたあらゆるイメージを信頼する KMS ポリシーを作成できるため、ポリシーを変更せずにイメージを更新できます。
Step 3: KMS アテステーションベースのキーポリシーを設定する
検証済みのエンクレーブに限定して復号を許可する KMS キーポリシーを作成します。
- PCR0(イメージハッシュ)を使用したポリシー: キーを特定のエンクレーブビルドに固定します。コードを変更するたびに新しい PCR0 が生成されるため、ポリシーの更新が必要になります:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowEnclaveDecrypt", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/EnclaveParentRole" }, "Action": [ "kms:Decrypt", "kms:GenerateDataKey" ], "Resource": "*", "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:ImageSha384": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210" } } } ] } - PCR8(署名証明書)を使用したポリシー: 特定の証明書で署名されたエンクレーブであればすべて信頼し、ポリシーを変更せずにイメージのローテーションを可能にします:
{ "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:PCR8": "ab3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890abcdef" } } } - 多層防御のための複数 PCR ポリシー: PCR0(イメージ)と PCR1(カーネル)を組み合わせ、アプリケーションとブート環境の両方が想定どおりの値と一致することを確認します:
{ "Condition": { "StringEqualsIgnoreCase": { "kms:RecipientAttestation:PCR0": "<pcr0-hex>", "kms:RecipientAttestation:PCR1": "<pcr1-hex>" } } } - IAM ロールポリシー: 親インスタンスの IAM ロールには
kms:Decrypt権限が必要ですが、KMS キーポリシーの条件により、有効なアテステーションドキュメントを添付したエンクレーブからのリクエストの場合にのみ実際の復号が成功します。
Step 4: セキュアな vsock 通信を実装する
親とエンクレーブの間の通信チャネルを確立します。
- vsock アーキテクチャ: エンクレーブが外部と通信する唯一の方法は vsock(仮想ソケット)を経由することです。vsock は CID(Context Identifier)とポート番号を使用します。親インスタンスの CID は常に
3であり、エンクレーブの CID は起動時に割り当てられます。 - 親側のプロキシサーバー: 親はプロキシを実行し、エンクレーブからの KMS API 呼び出しを vsock 経由で AWS KMS エンドポイントに転送します:
import socket import json import boto3 VSOCK_CID = 3 # Parent CID VSOCK_PORT = 5000 def start_proxy(): sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) sock.bind((VSOCK_CID, VSOCK_PORT)) sock.listen(5) kms_client = boto3.client('kms', region_name='us-east-1') while True: conn, addr = sock.accept() data = conn.recv(65536) request = json.loads(data.decode()) if request['action'] == 'decrypt': response = kms_client.decrypt( CiphertextBlob=bytes.fromhex(request['ciphertext']), Recipient={ 'KeyEncryptionAlgorithm': 'RSAES_OAEP_SHA_256', 'AttestationDocument': bytes.fromhex(request['attestation_doc']) } ) conn.sendall(json.dumps({ 'ciphertext_for_recipient': response['CiphertextForRecipient'].hex() }).encode()) conn.close() - エンクレーブ側のクライアント: エンクレーブアプリケーションは、
/dev/nsmにある Nitro Security Module(NSM)デバイスからアテステーションドキュメントを取得し、それを KMS の復号リクエストに添付して、エンクレーブの一時公開鍵で暗号化されたデータを受け取ります:import socket import json from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import hashes, serialization PARENT_CID = 3 VSOCK_PORT = 5000 def get_attestation_document(public_key_der): """Request attestation document from NSM device.""" # Uses the aws-nitro-enclaves-nsm-api # NSM provides: module_id, digest (SHA384), timestamp, PCRs, # certificate (from Nitro PKI), cabundle, public_key, user_data, nonce import nsm_util nsm_fd = nsm_util.nsm_lib_init() attestation_doc = nsm_util.nsm_get_attestation_doc( nsm_fd, public_key=public_key_der, user_data=None, nonce=None ) return attestation_doc def decrypt_via_parent(ciphertext_hex): """Send decrypt request through vsock to parent proxy.""" private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048 ) public_key_der = private_key.public_key().public_bytes( serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo ) attestation_doc = get_attestation_document(public_key_der) sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) sock.connect((PARENT_CID, VSOCK_PORT)) sock.sendall(json.dumps({ 'action': 'decrypt', 'ciphertext': ciphertext_hex, 'attestation_doc': attestation_doc.hex() }).encode()) response = json.loads(sock.recv(65536).decode()) sock.close() # KMS encrypted the plaintext to the enclave's public key # Only the enclave's private key can decrypt it ciphertext_for_recipient = bytes.fromhex( response['ciphertext_for_recipient'] ) plaintext = private_key.decrypt( ciphertext_for_recipient, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return plaintext
Step 5: アテステーションドキュメントを検証する
信頼を確立するために、エンクレーブからのアテステーションドキュメントを検証します。
-
アテステーションドキュメントの構造: ドキュメントは CBOR でエンコードされ、COSE_Sign1 で署名されています。含まれる項目は次のとおりです:
module_id: NSM モジュールの識別子digest: ハッシュアルゴリズム(SHA-384)timestamp: ドキュメントが作成された時点の Unix エポックミリ秒pcrs: PCR インデックスから測定値へのマップ(PCR0〜PCR15)certificate: Nitro PKI によって署名された NSM の x509 証明書cabundle: NSM 証明書から AWS Nitro ルート CA までの証明書チェーンpublic_key: エンクレーブの一時公開鍵(アテステーションのリクエスト時に提供)user_data: アプリケーションが定義する任意のデータ(最大 512 バイト)nonce: 鮮度を検証するための任意のノンス
-
検証手順:
- COSE_Sign1 構造をデコードし、ペイロードと証明書を取り出す
- 埋め込まれた証明書の公開鍵を使用して COSE 署名を検証する
- NSM 証明書から CA バンドルを経由し、AWS Nitro Attestation PKI のルート証明書(
https://aws-nitro-enclaves.amazonaws.com/AWS_NitroEnclaves_Root-G1.zipで入手可能)までの証明書チェーンを検証する - ルート CA 証明書が想定の AWS ルートと一致することを確認する:
aws.nitro-enclavesの CN - ドキュメントのタイムスタンプの時点で、チェーン内のどの証明書も期限切れでないことを確認する
- PCR0、PCR1、PCR2 の値を、エンクレーブビルドの出力から得た想定の測定値と比較する
- ノンスが提供されている場合は、リプレイ攻撃を防ぐためにノンスが一致することを確認する
-
アテステーション検証コード:
import cbor2 from cose import CoseMessage from cryptography import x509 from cryptography.x509.oid import NameOID def validate_attestation(attestation_bytes, expected_pcrs, expected_nonce=None): cose_msg = CoseMessage.decode(attestation_bytes) payload = cbor2.loads(cose_msg.payload) # Verify certificate chain cert = x509.load_der_x509_certificate(payload['certificate']) cabundle = [x509.load_der_x509_certificate(c) for c in payload['cabundle']] # Check root CA is AWS Nitro root = cabundle[-1] cn = root.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value assert cn == 'aws.nitro-enclaves', f'Unexpected root CA: {cn}' # Verify PCR measurements pcrs = payload['pcrs'] for idx, expected_value in expected_pcrs.items(): actual = pcrs.get(idx, b'').hex() assert actual == expected_value, f'PCR{idx} mismatch: {actual}' # Verify nonce freshness if expected_nonce: assert payload.get('nonce') == expected_nonce, 'Nonce mismatch' return payload
ステップ 6: エンクレーブの起動と監視
エンクレーブを実行し、運用監視を実装します:
-
エンクレーブを起動する:
nitro-cli run-enclave \ --eif-path enclave-app.eif \ --cpu-count 2 \ --memory 4096 \ --enclave-cid 16 \ --debug-mode注:
--debug-modeは開発用にエンクレーブコンソールを有効にします。エンクレーブの出力を読み取れるようになり、分離の保証が損なわれるため、本番環境では削除してください。 -
エンクレーブの状態を確認する:
nitro-cli describe-enclaves想定される出力には
"State": "RUNNING"、割り当てられたEnclaveCID、メモリ、CPU 数、エンクレーブのフラグが含まれます。 -
エンクレーブコンソールを読み取る(デバッグモードのみ):
nitro-cli console --enclave-id <enclave-id> -
エンクレーブを終了する:
nitro-cli terminate-enclave --enclave-id <enclave-id> -
CloudWatch モニタリング: 親インスタンスがエンクレーブの正常性メトリクスを報告するように構成します。エンクレーブにはネットワークアクセスがないため、ヘルスチェックは vsock プロキシを経由する必要があります:
# Parent-side health check over vsock def check_enclave_health(enclave_cid, port=5001): try: sock = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((enclave_cid, port)) sock.sendall(b'HEALTH_CHECK') response = sock.recv(1024) sock.close() return response == b'OK' except (socket.timeout, ConnectionRefusedError): return False
主要な概念
| 用語 | 定義 |
|---|---|
| Nitro Enclave | Nitro ベースの EC2 インスタンス上で Nitro Hypervisor によって作成される分離された仮想マシンです。永続ストレージ、ネットワークアクセス、対話型アクセスはなく、親インスタンスの root ユーザーであってもアクセスできません |
| Attestation Document | Nitro Security Module が生成する CBOR エンコードされ COSE で署名されたドキュメントです。PCR の測定値、AWS Nitro ルート CA までの証明書チェーン、任意のユーザー提供データを含みます |
| PCR (Platform Configuration Register) | エンクレーブのイメージ(PCR0)、カーネル/ブートストラップ(PCR1)、アプリケーション(PCR2)、IAM ロール(PCR4)、インスタンス ID(PCR3)、署名証明書(PCR8)を一意に識別する SHA-384 ハッシュの測定値です |
| Vsock | 親 EC2 インスタンスとそのエンクレーブの間の唯一の通信チャネルとなる仮想ソケットです。CID(Context Identifier)とポートでアドレス指定します |
| EIF (Enclave Image File) | nitro-cli が Docker イメージから構築するパッケージ化されたエンクレーブイメージです。カーネル、ramdisk、アプリケーションを含み、ビルド時に PCR の測定値を生成します |
| Nitro Security Module (NSM) | エンクレーブ内のカスタム Linux デバイス(/dev/nsm)です。アテステーションドキュメントの生成とハードウェア乱数生成を提供します |
| COSE_Sign1 | NSM の秘密鍵でアテステーションドキュメントに署名するために使用される、CBOR Object Signing and Encryption の単一署名者構造です |
| kms:RecipientAttestation | AWS KMS の条件キーのプレフィックスです。一致する PCR 値を持つ有効なアテステーションドキュメントが提示された場合にのみ復号/生成操作が成功するよう、キーポリシーで強制できます |
ツールとシステム
- nitro-cli: エンクレーブイメージファイルのビルド、エンクレーブの起動/終了、エンクレーブコンソール出力の読み取りを行う AWS CLI ツールです
- AWS KMS: Nitro Enclaves 向けのアテステーションベースの条件キーをネイティブにサポートする鍵管理サービスです。レスポンスはエンクレーブの一時公開鍵で暗号化されます
- aws-nitro-enclaves-sdk-c: エンクレーブ側の KMS 操作のための C SDK です。アテステーションドキュメントの生成と vsock プロキシ通信を処理します
- kmstool-enclave-cli: エンクレーブ内で実行され、アテステーション付きで KMS Decrypt と GenerateRandom 操作を行う、ビルド済みの CLI ツールです(SDK 付属)
- Nitro Enclaves ACM: エンクレーブ内で TLS 証明書をプロビジョニングし、HTTPS エンドポイントを確立する AWS Certificate Manager の統合機能です
- CloudTrail:
Recipientパラメータを含むDecryptやGenerateDataKeyなどの KMS API 呼び出しを記録し、エンクレーブ発の暗号操作の監査を可能にします
一般的なシナリオ
シナリオ: Nitro Enclave での PII トークン化サービスの実装
背景: ヘルスケア SaaS 企業が、PHI を含む患者レコードを処理しています。規制により、PHI の復号化とトークン化は、オペレーターがアクセスできるインスタンス上では決して行われてはなりません。同社は Nitro Enclave をデプロイし、この Enclave が暗号化された患者レコードを受け取り、アテステーションを伴う KMS を使用して Enclave 内で復号化し、PII フィールドをトークン化して、トークン化されたレコードのみを vsock 経由で返します。
アプローチ:
- トークン化ロジック、
kmstool-enclave-cliバイナリ、暗号化されたレコードを受け付ける vsock サーバーを含む Docker イメージとしてトークン化アプリケーションをビルドします nitro-cli build-enclaveで EIF をビルドし、ビルド出力から PCR0、PCR1、PCR2 を記録しますkms:RecipientAttestation:ImageSha384条件に PCR0 を一致させたキーポリシーを含む KMS キーを作成し、この特定の Enclave ビルドのみが患者レコードを復号化できるようにします- キーに対する
kms:Decrypt権限を持つ IAM ロールを付与して親インスタンスをデプロイしますが、KMS 条件により、復号化は認証済みの Enclave 内でのみ成功します - 親アプリケーションは HTTPS 経由で暗号化された患者レコードを受け取り、vsock ポート 5000 経由で Enclave に渡し、トークン化されたレコードを受け取ります
- Enclave は NSM にアテステーションドキュメントを要求し、それを KMS Decrypt 呼び出しに添付し、エフェメラル RSA キーで暗号化された平文を受け取り、ローカルで復号化して PII(SSN、DOB、名前)をトークン化し、
{ssn: "tok_a8f3...", dob: "tok_b2e1...", name: "tok_c9d4..."}を返します - CloudTrail のログには
RecipientAttestationパラメーター付きのDecrypt呼び出しが表示され、すべての復号化が Enclave の境界内で行われていることが確認できます
落とし穴:
- 本番環境で Enclave をデバッグモードで実行すると、コンソールへのアクセスが可能になり、規制当局が求める機密性の保証が損なわれます
- アテステーション条件を設けず、IAM ロールのみを使用するように KMS キーポリシーを設定すると、親インスタンスが Enclave を経由せずに直接復号化できてしまいます
allocator.yamlで十分なメモリを確保しないと、Enclave の起動時に不明瞭な「resource not available」エラーで失敗します- vsock のメッセージフレーミングを実装しないと、大きなレコードが 64KB のソケットバッファ境界で切り詰められます
- ビルドのたびに PCR0 が変わることを忘れると、デプロイのたびに KMS ポリシーの更新が必要になります。本番環境では、ビルドとポリシー更新を切り離すために PCR8(署名証明書)を使用してください
出力形式
## Nitro Enclave Security Assessment
**Enclave Image**: enclave-tokenizer.eif
**Build Date**: 2026-03-19T14:30:00Z
**Instance Type**: m5.2xlarge
**Allocated Resources**: 2 vCPUs, 4096 MiB memory
### PCR Measurements
| PCR | Value | Bound in KMS Policy |
|-----|-------|---------------------|
| PCR0 (Image) | a1b2c3d4e5f6... | Yes |
| PCR1 (Kernel) | f6e5d4c3b2a1... | Yes |
| PCR2 (Application) | 1a2b3c4d5e6f... | No |
| PCR8 (Signing Cert) | 9f8e7d6c5b4a... | Yes (production) |
### KMS Key Policy Verification
- Key ARN: arn:aws:kms:us-east-1:111122223333:key/mrk-abc123
- Attestation condition: kms:RecipientAttestation:ImageSha384 = PCR0
- Signing cert condition: kms:RecipientAttestation:PCR8 = <cert-hash>
- Parent role: arn:aws:iam::111122223333:role/EnclaveParentRole
- Direct decrypt from parent: BLOCKED (attestation required)
- Decrypt from verified enclave: ALLOWED
### Security Posture
- [PASS] Debug mode disabled in production launch command
- [PASS] Vsock is the only communication channel (no network interface)
- [PASS] Attestation document nonce verification implemented
- [PASS] Certificate chain validates to AWS Nitro root CA
- [WARN] PCR0 used in policy; consider PCR8 for deployment flexibility
- [FAIL] Health check endpoint does not verify enclave attestation freshness