はじめに
以前の記事で、オレオレ証明書のACMへの設定方法を紹介したが、この方法だとルート証明書の認証局がないため、場合によってはHTTPS接続でエラーになってしまう。
AWSでは、AWS Private Certificate Authorityを使うことで、お手軽に認証局の機能をお任せすることができるので、TerraformのIaCで自動化してサクッと構築したうえで、ACMと連動させられるようにしておこう。
AWS Private Certificate Authority
プライベート認証局の作成
まずは、Private Certificate Authorityの作成からだ。
local.acm_pca_common_name
には自分の取得しているドメインを設定しておこう。
permanent_deletion_time_in_days
は削除後にどれだけの期間、取消可能として残しておくかのパラメータだ。今回はお試しで作っているので、最短の7日を設定している。
なお、usage_mode
をSHORT_LIVED_CERTIFICATE(有効期間の短い証明書)
に設定すると、ACMからのリクエストがエラーになるので注意が必要だ。
resource "aws_acmpca_certificate_authority" "example" {
type = "ROOT"
usage_mode = "GENERAL_PURPOSE"
certificate_authority_configuration {
key_algorithm = "RSA_2048"
signing_algorithm = "SHA512WITHRSA"
subject {
common_name = local.acm_pca_common_name
}
}
enabled = true
permanent_deletion_time_in_days = 7
}
CA証明書のインストール
プライベート認証局を作ったら、CA証明書をインストールする。
特に難しいことはないが、validity(CA証明書の有効期間)
が、ACMの証明書発行のリクエストの期間よりも短いとエラーになることは留意しておこう。
また、template_arn
はTerraformのパラメータ上は必須ではないが、今回のケースでは付与していないとエラーになる。
resource "aws_acmpca_certificate" "example_root" {
certificate_authority_arn = aws_acmpca_certificate_authority.example.arn
certificate_signing_request = aws_acmpca_certificate_authority.example.certificate_signing_request
signing_algorithm = "SHA256WITHRSA"
template_arn = "arn:aws:acm-pca:::template/RootCACertificate/V1"
validity {
type = "YEARS"
value = 10
}
}
resource "aws_acmpca_certificate_authority_certificate" "example" {
certificate_authority_arn = aws_acmpca_certificate_authority.example.arn
certificate = aws_acmpca_certificate.example_root.certificate
certificate_chain = aws_acmpca_certificate.example_root.certificate_chain
}
ルート証明書をファイル出力する場合
ACMで設定するだけの場合は不要だが、ルート証明書を他のサーバに設定する場合はPEM形式のファイルが必要になるので、local_file
で出力しておこう。
resource "local_file" "example_root_cert_pem" {
filename = "${path.module}/certificate/example_root.pem"
content = aws_acmpca_certificate_authority.example.certificate
}
ACM
サーバ証明書のリクエスト
ACMは、ACM PCAに証明書のリクエストを送るだけであれば、以下で良い。とてもお手軽だ。
resource "aws_acm_certificate" "example" {
domain_name = local.acm_cert_domain_name
certificate_authority_arn = aws_acmpca_certificate_authority.example.arn
}
サーバ証明書のエクスポート
証明書のエクスポートはTerraformのAWS Providerでは機能提供されていないため、null_resource
改めterraform_data
でAWS CLIを実行する。
resource "terraform_data" "export_cert" {
provisioner "local-exec" {
command = <<-EOF
mkdir -p ./certificate &&
aws acm export-certificate --certificate-arn ${aws_acm_certificate.example.arn} --passphrase $(echo -n 'example' | openssl base64 -e) > ./certificate/tmp.json
EOF
}
}
エクスポートにはパスフレーズが必要だが、パスフレーズはBASE64エンコードされている必要があるので、煩雑だが上記のようにしておく。また、出力はサーバ証明書と秘密鍵と証明書チェーンがまとめて出てくるので、一旦JSONの状態のまま一時ファイルに吐き出す。
サーバ証明書、秘密鍵ファイルの作成
ということで、JSONのエクスポートファイルをjqコマンドで吐き出しておく。
なお、GitLabのように、証明書ファイルのパスフレーズを入力する機能がないサーバで扱う場合は、下記の通りOpenSSLのコマンドでパスフレーズを削除した秘密鍵ファイルを作っておく。パスフレーズの削除が不要な場合は、PrivateKey
の値を直接example.keyに出力しておこう。
resource "terraform_data" "parse_json_cert" {
depends_on = [terraform_data.export_cert]
provisioner "local-exec" {
command = <<-EOF
jq -r '.Certificate' ./certificate/tmp.json > ./certificate/example.pem &&
jq -r '.PrivateKey' ./certificate/tmp.json > ./certificate/example_pass.key &&
openssl rsa -in ./certificate/example_pass.key -out ./certificate/example.key
EOF
}
}
確認
サーバ証明書が発行されたら、以下のように中身を確認してみる。
ちゃんと証明書からルート証明書をたどれることが分かる。
これで、プライベート認証局で証明をしてくれるサーバ証明書の発行ができるようになった!
$ openssl x509 -text -noout -in ./certificate/example.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
6d:dc:6a:89:d8:dc:28:26:47:0a:ee:2f:52:34:bd:0f
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = ★ルート証明書のCommon Name
Validity
Not Before: Feb 18 06:14:05 2024 GMT
Not After : Mar 19 07:14:04 2025 GMT
Subject: CN = ★サーバ証明書のCommon Name
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:c1:5d:9e:ac:17:f3:b4:15:30:42:dc:9f:e5:c2:
ae:51:16:df:67:d5:3c:78:c2:52:27:b2:98:95:e5:
68:66:29:df:49:3a:52:7a:d4:50:6b:91:30:40:1d:
71:4b:c6:1b:13:79:df:93:36:3f:fd:2c:fe:b4:a3:
9f:23:70:89:1a:bd:c2:39:44:cc:9e:67:90:7a:6f:
b2:82:ce:b3:91:d6:5a:c9:7b:2e:21:4c:73:f2:16:
46:45:c3:2c:16:98:15:ac:84:e1:c3:ad:83:e5:7b:
e7:90:18:b0:56:47:aa:0b:e1:ba:95:cb:a8:46:5d:
8a:b7:15:2d:27:bd:2d:78:ba:79:b6:0e:22:91:13:
44:83:51:51:b0:ae:bf:43:3e:e2:db:de:c7:89:c0:
a6:04:76:93:90:cc:9e:30:72:52:60:70:83:f2:58:
37:08:9c:8d:62:c2:56:6a:06:15:13:86:e1:2a:0a:
a4:57:c3:47:f8:2e:a2:55:a2:21:f3:c2:6b:73:d5:
15:93:e9:75:bc:47:c5:82:35:88:51:49:7a:e6:f9:
85:2c:9e:e4:92:55:cd:23:4b:3e:14:9e:ae:4c:b6:
3f:cd:53:11:49:a6:db:bc:0b:00:d8:06:f9:8e:2a:
d8:79:e7:9a:e4:c5:77:35:75:b2:c0:fb:ee:2d:93:
4a:0f
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:★ドメイン名
X509v3 Basic Constraints:
CA:FALSE
X509v3 Authority Key Identifier:
keyid:53:A0:C6:14:D1:1C:C1:3F:0C:50:9E:B2:22:B0:A0:DF:6E:E3:22:CD
X509v3 Subject Key Identifier:
B2:65:1F:22:A1:6D:3D:CB:04:00:F9:2C:33:A5:ED:3E:F3:29:6B:4A
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
Signature Algorithm: sha256WithRSAEncryption
4d:d5:82:03:9f:d5:b0:8c:29:53:90:a5:68:0a:48:53:36:9e:
db:95:40:ff:4f:04:4e:af:58:b3:ca:e7:96:5d:c5:f3:19:a0:
00:12:d5:95:5b:90:17:ff:33:d4:6a:ed:40:f5:41:56:59:40:
c2:84:21:90:a1:36:c9:77:fc:a8:c4:34:66:95:8c:88:ca:8a:
03:c8:e6:b3:96:1c:da:e0:17:b0:46:dc:01:ec:24:3a:34:13:
28:ff:47:e5:9f:ff:4a:8d:02:57:95:01:90:f4:9a:17:98:53:
6e:76:98:0b:2b:6b:5d:54:51:89:c8:26:aa:b7:af:e6:60:9d:
83:2c:49:24:d8:65:74:be:2b:ad:0f:b9:cf:91:d5:31:ad:06:
df:77:ef:bf:d5:98:d5:56:38:85:cc:05:70:93:52:9c:e1:17:
ea:6c:18:ea:25:e4:d0:ef:40:8b:3b:fd:01:33:bb:08:d0:c4:
54:d7:da:bd:b8:52:ca:e2:c0:11:89:64:ac:6e:25:d3:b4:9a:
b9:46:d5:12:3a:0e:02:36:be:9a:5c:d5:e1:d7:ec:d6:54:8d:
4d:da:c5:93:65:d6:ae:50:eb:87:f6:62:8a:18:eb:1c:ab:27:
e8:57:83:91:f3:77:af:8c:26:4a:b8:7f:fe:fb:3e:ce:cf:ab:
9e:43:b1:b6