エンタープライズクラウド部 技術3課の山本拓海です。
好きなプリンスのアルバムは「Prince」です。
このPVの大学生みたいな風貌のプリンスが、後のイメージと大きくかけ離れていて、なんかかわいくて好きです。
さて。
先日AWSセキュリティブログにてポストされた「Set up AWS Private Certificate Authority to issue certificates for use with IAM Roles Anywhere」という記事の内容が大変興味深いものでした。
こちらの内容を深掘りして理解を深めます。
- イントロ 記事の内容で何を解決できるか
- アーキテクチャの解説
- 費用について
- 準備と構築
- CertCheck-Trigger関数の実行と実行後の確認
- 証明書を失効する
- 証明書の手動ローテーション
- 最後に
イントロ 記事の内容で何を解決できるか
元ポストの構成は、AWSリソースを使い、IAM Roles Anywhereで使用する、AWS外デバイスのエンドエンティティ証明書を自動で更新するシステムです。 このシステムは実際の運用を考慮された構成になっており、IAM Roles Anywhereの実運用構成の1つのリファレンスになると思います。
この構成を使うことで、システム用のIAM Userを発行する必要がなくなるので、長期認証情報によるセキュリティの脆弱性が減らせることがメリットです。
1点だけ気になるのは、実行する全てのコマンドがAWSのCloudShellと運用するAWS外デバイスの2環境だけで対応ができない点です。
CloudFormationの実行や準備に、git, AWS CLIを準備済みの特定のS3バケットに書き込み権限を持った環境が必要になります。
ただ、このままでも十分に実運用可能なので、今回はこのまま行います。
IAM Roles Anywhereの仕組み
IAM Roles Anywhereの仕組みや関係性の理解するのにあたり、AWS Private CAとIAM Roles AnywhereとAWS外デバイスの関係を整理した図が以下の通りです。
IAM Roles AnywhereとAWS Private CAの連携についてより詳しい内容は、以下の記事をご覧ください。
アーキテクチャの解説
上述した以前の弊社の記事で手動で行っている、署名要求(CSR)と秘密鍵の発行、エンドエンティティ証明書のデプロイを1日2回実行するEventBridgeからキックされたLambda関数で自動で行います。
構成図に番号が降ってありますが、基本的には3つあるLambda関数からエンドエンティティ証明書発行までの全てのプロセスを実行しています。 それぞれのLambda関数がどのプロセスを実行しているか、詳しく見ていきます。
以下の関数で紹介している番号は構成図内の番号です。
1. CertCheck
EventBridgeで1日2回キックされるCertCheck関数では、②③の処理でDynamoDBのテーブルをスキャンし、証明書管理が必要なインスタンスを特定を行い、有効期限が切れているデバイスがあれば、CertIssue関数をキックします。(④)
2. CertIssue
CertCheck関数で証明書の有効期限が切れたデバイスがある場合、CertIssue関数がキックされ、SSM Run Command経由でCSRと秘密鍵生成のコマンドをAWS外デバイス内で実行します。(⑥)
⑦の処理内のコマンドでCSRと秘密鍵を生成し、⑧⑨の処理では生成したCSRを用いてAWS Private CAにて証明書に署名をリクエストし、エンドエンティティ証明書を発行します。
3. CertDeploy
CertIssue関数実行時にEventBridgeにて証明書の発行イベントを検知したら(⑩)CertDeploy関数に発行したエンドエンティティ証明書のARNを渡して、関数をキックします(⑪)。
CertDeploy関数内で、エンドエンティティ証明書をAWS Private CAから取得し、デバイスにSSM Run Command経由でエンドエンティティ証明書のCRTファイルを書き込みます。(⑫⑬)⑫では同時にDynamoDB上の有効期限を書き換えます。
CertDeploy関数実行後、デバイス上には、エンドエンティティ証明書と秘密鍵があるので、IAM Roles AnywhereからIAM Roleを付与できる状態となります。(⑭)
費用について
本システムの実行に必要なAWSリソースと費用です。
元ポストに以下の記述があるので、参考にしてください。
100 台のホストの証明書を管理する場合、月額約 85 ドルがかかります。ただし、Systems Manager のアドバンスト・ティアで 1,100 台のホストを大規模に導入する場合、コストは月額約 5937 ドルとなります
1100台のホストで費用が高くなるのは、ハイブリッドアクティベーションにて管理するデバイスが1000台以上は課金されるためです。
東京リージョンでの費用の時間あたりの単価は以下の通りです。
一番費用が大きいサービスが、AWS Private CA の 月額50USDですが、プライベート CA の運用が 1 か月に満たない場合、CA を作成および削除した時点に基づいて按分計算されるので、お試しで構築する場合は50USDはかかりません。
- EventBridge
- スケジューラーでの実行は月間 14,000,000 回の呼び出しを無料
- AWS SSM Run Command
- 無料
- AWS SSM ハイブリッドアクティベーション
- アカウントごとにリージョンあたり最大 1,000 までの無料
- IAM Roles Anywhere
- 無料
- Lambda
- 0.001002 USD / 1分
- Dynamo DB
- 書き込み要求単位 (WRU) 書き込み要求ユニット 100 万あたり 1.4269USD
- 読み出し要求単位 (RRU) 読み出し要求ユニット 100 万あたり 0.285USD
- AWS Private CA
- 有効期間の短い証明書モードの場合、プライベート CA あたり月額 50 USD
- 1 個以上の証明書 0.058 USD
準備と構築
環境の説明
今回使用する環境は、以下の通りです。
- AWS外デバイス
- IAM Roles AnywhereでRoleを付与する環境
- 記事の実行環境はUbuntu 22.04.2を使用
- 個人ローカル環境
- CloudFormationなどの管理・編集を行うローカル環境
- CloudShell
- Lambda関数の初回実行などを行う環境
準備
元ポストのPrerequisitesとEnabling Systems manager hybrid activationの箇所の説明は省きます。
hybrid activationが完了すると、AWS外デバイスにSSM Run Commandが実行できるようになります。手順に沿って進めてください。
詳しくは以前の弊社のブログを参考に進めてください。
構築
git, AWS CLIが使用できる個人ローカル環境で行います。
構築での作業は以下の通りです。
- gitで構築するリポジトリをclone
- IAM Roleの編集とCloudformation packageコマンドを実行
- Cloudformation deployコマンドを実行
gitで構築するリポジトリをclone
リポジトリを clone します。
git clone https://github.com/aws-samples/aws-privateca-certificate-deployment-automator.git
IAM Roleの編集とCloudformation packageコマンドを実行
AWS CLIにてCloudformation packageコマンドを実行します。
Cloudfomation Packageコマンドについては、以下のページに詳しい説明があります。
CloudFormation packageコマンド実行前に、デバイスに付与するIAM Roleを編集します。 IAM Roleのポリシーは元の状態だと全てDenyになっています。今回は特定のS3バケットを操作できるよう、フルコントロール可能なポリシーをアタッチします。
cf_template.yamlの77行目付近の、リソースIAMRARoleのPolicies属性を書き換えます。
コメントアウトしているのが元の記述です。
Policies: # - PolicyName: DenyAll - PolicyName: Allow-s3-fullcontrol PolicyDocument: Version: '2012-10-17' Statement: # - Effect: Deny # Action: '*' # Resource: '*' - Effect: Allow Action: 's3:*' Resource: 'arn:aws:s3:::iam-ra-handson-sample-202311/target-folder/*'
書き換えたら、CloudFormation packageコマンドを実行します。オプション s3-bucketの引数はCloudFormation用のファイルを置くバケット名を入力します。
aws cloudformation package --template-file cf_template.yaml \ --output-template-file packaged.yaml \ --s3-bucket ${S3_BUCKET_NAME}
CloudFormation packageコマンドが完了し、packaged.yaml が作成されていることを確認します。さらに、必要なファイルがs3にアップロードされているのを確認します。
Cloudformation deployコマンドを実行
CloudFormation deployコマンドで構築します。 CloudFormation deployコマンドで、指定できるパラメータは以下の通りです。適宜変更します。
パラメータキー | デフォルト値 | 用途 |
---|---|---|
AWSSigningHelperPath | /root | AWS Signing Helperバイナリのホスト上のデフォルトパス |
CACertPath | /tmp | CA 証明書が作成されるホストのデフォルトパス |
CACertValidity | 10 | デフォルトの CA 証明書の長さ(年 |
CACommonNam | ca.example.com | デフォルトの CA 証明書コモンネーム |
CACountry | US | デフォルトの CA 証明書の国コード |
CertPath | /tmp | 証明書が作成されるホストのデフォルトパス |
CSRPath | /tmp | 証明書が作成されるホストのデフォルトパス |
KeyAlgorithm | RSA_2048 | CA 秘密鍵の作成に使用されるデフォルト・アルゴリズム 使用できる値 - RSA_2048 - EC_prime256v1 - EC_secp384r1 - RSA_4096 - EC_secp384r1 - RSA_4096 |
KeyPath | /tmp | 秘密鍵が作成されるホストのデフォルトパス。 |
OrgName | Example Corp | デフォルトの CA 証明書組織名 |
SigningAlgorithm | SHA256WITHRSA | 発行された証明書のデフォルトの CA 署名アルゴリズム 使用できる値 - SHA256WITHECDSA - SHA256WITHRSA - SHA384WITHECDSA - SHA384WITHRSA - SHA512WITHECDSA - SHA512WITHRSA |
CACommonNamとCACountryとOrgNameを変更します。今回は以下のコマンドで実行します。
aws cloudformation deploy --template packaged.yaml \ --stack-name ssm-pca-stack \ --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides CACommonNam=serverworks.co.jp \ CACountry=JP \ OrgName=Serverworks \ SNSSubscriberEmail=takumi.yamamoto@serverworks.co.jp
3分ほどで構築完了。構築物の内容を確認していく前に、不意に実行されないようにするためEventBridgeのスケジュール実行を無効化しておきます。
続いて、証明書の発行前に以下の準備を行います。
- SNSのサブスクリプション
- DynamoDBの準備
SNSのサブスクリプションの確認の説明は省きます。
DynamoDBの準備を行います。 元ポストの手順に沿って、Fleet ManagerからNode IDをコピーし、DynamoDBにIDを投入します。さらに、元ポストに記載のある、サポートする属性を入力し、CloudFormationの内容をオーバーライドしてみます。
今回はSingning Helperのパスを上書きします。属性の型はstring型です。
ここまででLambda関数実行前の準備は完了です。
CertCheck-Trigger関数の実行と実行後の確認
以下のコマンドでCertCheck-Trigger関数を実行し、証明書の発行を行います。
CertCheck-Trigger関数の実行は、利用するAWSアカウントのCloudShellから行います。十分な権限を持つユーザーで進めてください。
aws lambda invoke --function-name CertCheck-Trigger \ --cli-binary-format raw-in-base64-out \ response.json
ここでのresponse.jsonの内容は 「null」だけです。
以下は初回の実行後の各Lambda関数のログです。読みにくいので実行時間とリクエストIDは省略。
ロググループ /aws/lambda/CertCheck-Trigger の内容
[INFO] Starting the certificate check process [INFO] Found credentials in environment variables. [INFO] No expiry found for host: mi-0d2xxxxxxxxx1b5. A certificate will be issued. [INFO] Invoked the certificate issue lambda for host: mi-0d2xxxxxxxxx1b5
ロググループ /aws/lambda/CertIssue の内容
[INFO] Found credentials in environment variables. [INFO] Incoming event: {'hostID': 'mi-0d2xxxxxxxx1b5', 'certPath': '/tmp', 'keyPath': '/tmp'} [INFO] Certificate signing issued to PCA successfully for host: mi-0d2xxxxxxxx1b5 [INFO] {'CertificateArn': 'arn:aws:acm-pca:ap-northeast-1:958xxxxxxx246:certificate-authority/ec4403de-xxxxxxxxxxxx52d7/certificate/b8a7bf5570c7d25c07dd0f0b781ce8da', 'ResponseMetadata': {'RequestId': 'request-id-sample', 'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Thu, 23 Nov 2023 07:47:59 GMT', 'content-type': 'application/x-amz-json-1.1', 'content-length': '168', 'connection': 'keep-alive', 'x-amzn-requestid': 'request-id-sample'}, 'RetryAttempts': 0}}
ロググループ /aws/lambda/CertDeploy の内容
[INFO] Extracted details - Common Name: mi-0d2xxxxxxxxx1b5, Serial: 151xxxxxxxxxxx8E0, Expiry: 2023-11-30 08:03:23 [INFO] Using paths - cacert_path: /tmp, key_path: /tmp, cert_path: /tmp, AWSSigningHelperPath: /home/user-1 [INFO] SSM Command - ID: 8a90f87b-fb49-4956-bfd6-bff7782abbdb, Status: Success, Output: [INFO] DynamoDB update details - Host ID: mi-0d2xxxxxxxxx1b5, Serial: 151xxxxxxxxxxx8E0, Expiry: 2023-11-30 08:03:23
CertDeployのログにSuccessとあるので、AWS外デバイスへの証明書のデプロイが成功しているようです。
DynamoDBのレコードの確認です。
有効期限とシリアルNoの項目が追加されています。
デバイス内のファイルの確認と認証ヘルパーコマンドの実行
AWS外デバイス内の各ファイルの確認を行います。
user-1@ubuntu-1:~$ ls -la /tmp drwxrwxrwt 15 root root 4096 Nov 23 17:03 . drwxr-xr-x 21 root root 4096 Jul 25 20:14 .. -rw-r--r-- 1 root root 1196 Nov 23 17:03 ca_chain_certificate.crt -rw-r--r-- 1 root root 1432 Nov 23 17:03 mi-0d2xxxxxxxxx1b5.crt -rw-r--r-- 1 root root 907 Nov 23 17:03 mi-0d2xxxxxxxxx1b5..csr -r-------- 1 root root 1708 Nov 23 17:03 mi-0d2xxxxxxxxx1b5..key
IAM Roles AnywhereでIAM Roleを付与できるファイルが揃っています。 実際に認証ヘルパーコマンドを実行し、認証情報を取得してみます。
認証ヘルパーコマンドの詳細な情報は以下のリンクをご確認ください。
認証ヘルパーコマンド実行の際に使用する、信頼アンカーのARNとプロファイルのARNはIAM Roles Anywhereのダッシュボードから遷移できる詳細画面内にあります。
認証ヘルパーコマンドを実行します。実行結果はjsonで返ってくるので jqで整形します。
sudo /home/user-1/aws_signing_helper credential-process --certificate /tmp/mi-0d2xxxxxx1b5.crt \ --private-key /tmp/mi-0d2xxxxxx1b5.key \ --trust-anchor-arn ${TRUST-ANCHOR-ARN} \ --profile-arn ${PROFILE-ARN} \ --role-arn ${IAM-ROLE-ARN} | jq . # 実行結果 # { # "Version": 1, # "AccessKeyId": "ACCESSKEYID-SAMPLE", # "SecretAccessKey": "SECRETACCESSKEY-SAMPLE", # "SessionToken": "SESSIONTOKEN-SAMPLE==", # "Expiration": "2023-11-23T09:07:59Z" # }
認証情報を取得できました。
このコマンドをAWS CLIのconfig情報に記述することで、実行結果の認証情報を使用できるので、合わせて設定します。
AWS CLIコマンドを実行するユーザのホームフォルダ内に.aws/configを作成し、defaultプロファイルに認証ヘルパーコマンドを記載します。
既にdefaultプロファイルがある場合、適宜変更してください。
[default] region = ap-northeast-1 output = json credential_process = sudo /home/user-1/aws_signing_helper credential-process --certificate /tmp/mi-0d2xxxxxx1b5.crt --private-key /tmp/mi-0d2xxxxxx1b5.key --trust-anchor-arn ${TRUST-ANCHOR-ARN} --profile-arn ${PROFILE-ARN} --role-arn ${IAM-ROLE-ARN}
設定できているか確認します。
aws sts get-caller-identity # 実行結果 # { # "UserId": "AROAxxxxxxxxHDC:15133f67xxxxxxxxxxxxea2e8e0", # "Account": "958xxxxx246", # "Arn": "arn:aws:sts::958xxxxx246:assumed-role/IAMRA-Role/15133f67xxxxxxxxxxxxea2e8e0" # }
この認証使って、特定のS3バケットへの書き込みとファイルのダウンロードを行います。
aws s3 cp ./test.txt s3://iam-ra-handson-sample-202311/target-folder/ # upload: ./test.txt to s3://iam-ra-handson-sample-202311/target-folder/test.txt
無事成功です。 当然別フォルダに対しては権限がないはずなので、こちらも確認します。
aws s3 cp ./test.txt s3://iam-ra-handson-sample-202311/not-target-folder/ # upload failed: ./test.txt to s3://iam-ra-handson-sample-202311/not-target-folder/test.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
想定通りできません。
証明書を失効する
エンドエンティティの証明書を失効させて、IAM Roleが付与されないようにしてみます。
元ポストの構成図です。
AWS CLIコマンドにてAWS Private CAで証明書失効リスト(CRL)を発行後、発行イベントを検知したEventBridgeが、LambdaのCRL Processor関数をキックします。CRL Processor関数でIAM Roles Anywhereに失効証明書をインポートすることでエンドエンティティ証明書を失効させます。
失効はコマンドだけで完結しますが、その後の状態を確認したいので以下の手順で行います。
- (準備 )AWS Private CAの監査レポート出力先S3バケットにバケットポリシーを付与する
- CloudShellからエンドエンティティ証明書失効コマンドを実行する
- 発行されたCRLファイルを確認する
- AWS Private CAの監査レポートを確認し、失効されていることを確認する
準備
AWS Private CAの監査レポート出力先S3バケットにバケットポリシーを付与する
エンドエンティティ証明書失効後にAWS Private CAの監査レポートから失効されていることを確認します。監査レポートの出力先のS3バケットにバケットポリシーの付与が必要です。 以下のURLを参考に行ってください。
実行
CloudShellからエンドエンティティ証明書失効コマンドを実行する
失効はCloudShellからコマンドを実行します。
aws acm-pca revoke-certificate \ --certificate-authority-arn <certificate-authority-arn> \ --certificate-serial <certificate-serial> \ --revocation-reason CESSATION_OF_OPERATION
発行されたCRLファイルを確認する
コマンド実行後、30分以内にAWS Private CAはCRLファイルを生成し、CloudFormationテンプレートで作成されたCRL S3バケットのcrlフォルダにアップロードします。 crlファイルをダウンロードし、以下のコマンドで内容を確認します。
openssl crl -inform DER -in ${CRL_FILE} -text -noout
エンドエンティティ証明書が失効している場合、以下の内容が出力されています。
Revoked Certificates: Serial Number: ${SERIAL_NUMBER} Revocation Date: Nov 24 01:22:40 2023 GMT CRL entry extensions: X509v3 CRL Reason Code: Cessation Of Operation
また、Cloudwatch Logsの/aws/lambda/CRLProcessorにもCRL Processor関数の実行ログが出力されているので確認します。詳細は省きます。
AWS Private CAの監査レポートを確認し、失効されていることを確認する
監査レポートの確認を行います。 監査レポートの出力は、AWS Private CAの画面から行えます。
監査レポートに以下の内容が含まれています。
{ "awsAccountId": "958xxxxxx246", "certificateArn": "arn:aws:acm-pca:ap-northeast-1:958xxxxxx246:certificate-authority/ec4403xxxxxxxxxxxxxxx5c52d7/certificate/6ec39cf34c73ec550ef3d380b9495e09", "serial": "6e:c3:xx:xx:xx:xx:xx:xx:xx:xx:5e:09", "subject": "CN=mi-0d2xxxxxxxxx1b5", "notBefore": "2023-11-23T23:35:51+0000", "notAfter": "2023-12-01T00:35:51+0000", "issuedAt": "2023-11-24T00:35:52+0000", "revokedAt": "2023-11-24T01:22:40+0000", "revocationReason": "CESSATION_OF_OPERATION", "templateArn": "arn:aws:acm-pca:::template/EndEntityCertificate/V1" }
失効されていることが確認できました。
証明書の手動ローテーション
証明書の手動ローテーションは、DynamoDBのレコードにある有効期限をNullにした後、エンドエンティティ証明書発行コマンドを実行するとローテーション可能です。
ここまでの内容に記載があるので、詳細は省きます。
最後に
AWSセキュリティブログ「Set up AWS Private Certificate Authority to issue certificates for use with IAM Roles Anywhere」で紹介されている内容について深掘りしていきました。
本システムを実環境で運用される場合の注意点として、EventBriegeのスケジュール実行を最初に止めています。
スケジュール実行で行いたい場合は再度有効化してください。
ご一読いただきありがとうございました。
山本 拓海(執筆記事の一覧)
エンタープライズクラウド部 クラウドリライアビリティ課
Security Hubになりたい
写真は黒猫のくま。
記事に関するお問い合わせや修正依頼⇒ takumi.yamamoto@serverworks.co.jp