AWS CDKを使ってAWS Private Certificate Authority(AWSプライベートCA)の認証機関を作成して有効化する
問題
AWS Private Certificate Authority(AWSプライベートCA)の認証機関を作成することになりました。
当然こういったものはAWS CDKを使って管理したいものです。短命な鍵オンリーなモードでもあるだけで月額50ドル飛んで行くそうなので、なおさらですね。
ただ、 aws-cdk-lib.aws_acmpcaでは、高水準な抽象化がされたクラスでの生成管理が出来なくて、 Cfnレベルのリソースを使う必要があってかなり調べるのに苦労したので、メモしておきます。
認証機関を作るだけでは使えない
認証機関を作るだけならば、 CfnCertificateAuthorityを、適当にnewしてやれば終了です。
ただそれだと保留中の認証機関が出来るだけで、ルートCAも作成されず、様々な操作が出来ません。
手動でwebコンソールをcdk deployの間に叩きに行くとかは絶対に避けたい話ですね。
なので以下の3つのクラスを連携させる必要があります。
ルートCA証明書を作成するためにはテンプレート引数が必要
コンソールでは「CA証明書をインストール」となっているのでそれをすれば良いのだろうと思って、
new CfnCertificate
したのですが、
The certificate authority is not in a valid state for issuing certificates (Service: AWSACMPCA; Status Code: 400; Error Code: InvalidStateException;…)
というエラーに悩まされ続けてきました。
例外処理 - AWS Certificate Manager には確かにこのエラーが書かれているのですが、待機中の認証機関にが証明書を発行出来ないというわけで、でも待機を解くには証明書をインストールしないといけないというジレンマに陥りました。
もがき苦しんだ結果、
Creating and installing the CA certificate - AWS Private Certificate Authority
で、
--template-arn arn:aws:acm-pca:::template/RootCACertificate/V1
が指定されているのを見て、
CAデフォルトの証明書を作るにはtemplateArn
プロパティに特定のテンプレートの指定が必要だとやっと分かりました。
確かに書いてはあったんですが、誰もこれをやっていなかったのか、サンプルコードが全然転がってなくて該当の引数を見つけるのに苦労しました。
認証機関生成サンプルコード
引数の受け渡しとかもちょっと微妙にわかりにくいので、スタックの例を書いておきます。
import { Stack, StackProps } from "aws-cdk-lib";
import {
CfnCertificate,
CfnCertificateAuthority,
CfnCertificateAuthorityActivation,
} from "aws-cdk-lib/aws-acmpca";
import { HostedZone } from "aws-cdk-lib/aws-route53";
import { Construct } from "constructs";
interface Props extends StackProps {
rootZone: HostedZone;
}
/**
* 短命限定で証明書を発行するAWS Private Certificate Authorityを立ち上げる。
* 短命限定モードだと比較的安いとは言え、存在しているだけで月50ドルのそれなりのコストがかかる。
*/
export default class ShortLivedPrivateCaStack extends Stack {
cfnCertificateAuthority: CfnCertificateAuthority;
cfnCertificate: CfnCertificate;
cfnCertificateAuthorityActivation: CfnCertificateAuthorityActivation;
constructor(scope: Construct, id: string, props: Props) {
super(scope, id, props);
const { rootZone } = props;
// 高水準なclassがfromで管理外から引っ張ってくる方法以外存在しないようなので、
// 仕方なくCfnの低水準なAPIを利用。
// 認証機関を生成。
this.cfnCertificateAuthority = new CfnCertificateAuthority(
this,
"CertificateAuthority",
{
type: "ROOT", // サブドメインを利用しているわけではない。
keyAlgorithm: "好きなアルゴリズムを指定してください",
signingAlgorithm: "好きなアルゴリズムを指定してください",
subject: {
commonName: rootZone.zoneName,
// 適当に証明書の情報を書いておいてください。
},
usageMode: "SHORT_LIVED_CERTIFICATE", // 短命専用モードを利用。
},
);
// 認証機関向けにルート証明書を発行。
this.cfnCertificate = new CfnCertificate(this, "Certificate", {
certificateAuthorityArn: this.cfnCertificateAuthority.attrArn,
certificateSigningRequest:
this.cfnCertificateAuthority.attrCertificateSigningRequest,
signingAlgorithm: this.cfnCertificateAuthority.signingAlgorithm,
validity: {
type: "YEARS",
value: 証明書の長さを指定,
},
templateArn: "arn:aws:acm-pca:::template/RootCACertificate/V1",
});
// 認証機関を有効化。
this.cfnCertificateAuthorityActivation =
new CfnCertificateAuthorityActivation(
this,
"CertificateAuthorityActivation",
{
certificate: this.cfnCertificate.attrCertificate,
certificateAuthorityArn: this.cfnCertificateAuthority.attrArn,
},
);
}
}