AWS Private CA のマルチアカウント CA 階層設計について考える
はじめに
前回の記事では、AWS Private CA の証明書発行方法 (API 経由と ACM 連携) を比較しました。
今回は実運用を見据え、「CA をどう構成すべきか」という設計面に焦点を当てます。
ルート CA が侵害されると全証明書の信頼性が失われるため、AWS ではマルチアカウント構成による CA 階層設計がベストプラクティスとして推奨されています。
本記事では、なぜこの構成が推奨されるのか、メリット・デメリットを整理し、実際に構築して気づいた点を共有します。
CA 階層とは
CA 階層は、PKI において信頼の連鎖を構築する仕組みです。
ルート CA → 中間 CA → エンドエンティティ証明書という階層構造を持ち、証明書の信頼性は証明書チェーンを遡って検証されます。
AWS のベストプラクティスでは、ルート CA は中間 CA 証明書の発行のみに使用し、エンドエンティティ証明書は中間 CA から発行することが推奨されています。
CA 階層の構造をイメージ図にすると以下のようになります。

これにより、ルート CA を厳重に保護しつつ、日常的な証明書発行は中間 CA で行う役割分担が実現できます。
マルチアカウント構成の設計
アーキテクチャ概要
AWS Well-Architected Framework では、CA 階層の各レベルを別アカウントに配置することを推奨しています。
今回は以下のような構成で実装してみました。

AWS RAM によるクロスアカウント共有
中間 CA 証明書をルート CA に発行してもらうには、AWS Resource Access Manager (RAM) などを使用してルート CA を共有する必要があります。
RAM でマネージド型アクセス許可を関連付けることができますが、ポリシーの選択には注意が必要です。
今回の場合、中間 CA からルート CA に acm-pca:IssueCertificate で証明書を発行してもらう必要があります。
そのため、下位 CA に署名するときのテンプレート SubordinateCACertificate_PathLen0/V1 に許可しているポリシーを選びました。

Private CA におけるリソースベースポリシーについては以下の記事を参照してください。
パスレングス制約について
なお、中間 CA 証明書発行時に SubordinateCACertificate_PathLen0/V1 テンプレートを使用すると、パスレングスが 0 に設定されます。
これにより、中間 CA からは更なる下位 CA を作成できず、エンドエンティティ証明書のみ発行可能になります。
パスレングス制約は証明書作成後に変更できないため、設計段階で考慮すべきポイントになりますね。
ルート CA と中間 CA の準備
実際にマルチアカウント構成で CA 階層を構築しました。
ルート CA は前回の記事で作成したものをそのまま使います。
RAM などによるリソースの共有設定が済んでいれば、以下のように中間 CA を作成したアカウントでもルート CA の情報を閲覧できるようになります。

中間 CA は以下のようにしておきました。CN は intermediate.example.com としています。

検証してみた
やることは前回の記事とほぼ同じで、
- 秘密鍵と CSR の作成
- 中間 CA からエンドエンティティ証明書を発行
- エンドエンティティ証明書と証明書チェーンを取得
という流れです。
CSR と秘密鍵は以下のような内容で作成しました。
$ openssl req -out intermediate-csr.pem -new -newkey rsa:2048 -nodes -keyout intermediate-private-key.pem
...
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:WA
Locality Name (eg, city) []:Seattle
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Corp
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:app.example.com
作成した CSR を中間 CA に渡して、エンドエンティティ証明書を発行します。有効期限は 1 年です。
$ aws acm-pca issue-certificate \
--certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:222222222222:certificate-authority/foo \
--csr fileb://intermediate-csr.pem \
--signing-algorithm "SHA256WITHRSA" \
--validity Value=1,Type="YEARS"
{
"CertificateArn": "arn:aws:acm-pca:ap-northeast-1:222222222222:certificate-authority/foo/certificate/bar"
}
上記で発行された ARN を指定して、エンドエンティティ証明書と証明書チェーンをそれぞれ取得します。
$ aws acm-pca get-certificate \
--certificate-arn arn:aws:acm-pca:ap-northeast-1:222222222222:certificate-authority/foo/certificate/bar \
--certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:222222222222:certificate-authority/foo \
--output text --query 'Certificate' > intermediate-server.crt
$ aws acm-pca get-certificate \
--certificate-arn arn:aws:acm-pca:ap-northeast-1:222222222222:certificate-authority/foo/certificate/bar \
--certificate-authority-arn arn:aws:acm-pca:ap-northeast-1:222222222222:certificate-authority/foo \
--output text --query 'CertificateChain' > intermediate-ca-cert.crt
取得したエンドエンティティ証明書をサーバに設置して、クライアントから接続してみましょう。
サーバ用スクリプトは前回記事と同様ですが、証明書と秘密鍵の箇所は今回取得・作成したものに置き換えています。
import ssl
import http.server
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('intermediate-server.crt', 'intermediate-private-key.pem')
server = http.server.HTTPServer(('localhost', 8443), http.server.SimpleHTTPRequestHandler)
server.socket = context.wrap_socket(server.socket, server_side=True)
print("TLS server running on https://localhost:8443")
server.serve_forever()
サーバを起動して、リクエストを送ってみます。
$ python3 server.py
TLS server running on https://localhost:8443
===
$ curl -Iv --cacert ca.crt --resolve app.example.com:8443:127.0.0.1 https://app.example.com:8443
...
curl: (60) SSL certificate problem: unable to get local issuer certificate
ダメでした。エラーメッセージを見ると「unable to get local issuer certificate」と出ています。
これは証明書チェーンの検証に失敗したことを意味します。今回、クライアント側では --cacert ca.crt でルート CA 証明書を指定していますが、中間 CA 証明書については指定していません。
TLS/SSL の仕組みでは、クライアントが持っているのはルート CA 証明書のみで、中間 CA 証明書はサーバ側から送信される必要があります。
つまり、サーバ側はエンドエンティティ証明書だけでなく、中間 CA 証明書も含めた証明書チェーンを返す必要があるわけです。
というわけで、エンドエンティティ証明書と中間 CA 証明書を組み合わせた証明書を作成します。
cat intermediate-server.crt intermediate-ca-cert.crt > intermediate-fullchain.crt
これをサーバ側に設置することで、正しく通信できるようになります。
おわりに
マルチアカウント CA 階層構成は、セキュリティと運用性を両立させるベストプラクティスです。
ルート CA を専用アカウントで管理することで、侵害リスクを最小化できます。
一方で、コスト面には注意が必要です。プライベート CA あたり月額 400 USD が発生するので、ルート CA と中間 CA を構築すると合計で月額 800 USD となります。
決して無視できない金額ですので、求められているセキュリティ要件や、コストとリスクを天秤に掛けて、慎重に検討する必要があるでしょう。






