🔑

HSM と Vault を連携して PKI シークレットエンジンで利用する秘密鍵の管理をする

2024/07/12に公開

VaultのPKIシークレットエンジンやTransitシークレットエンジンで扱う秘密鍵をVaultのバックエンドストレージではなく、外部のキー管理システムで管理したいという要件があるケースがあるかと思います。

Vault Enterpriseで利用できるマネージドキーの機能を利用する事で、この要件を実現する事ができます。
https://developer.hashicorp.com/vault/docs/enterprise/managed-keys

以下は、マネージドキーを利用し、PKIシークレットエンジンを設定し、その環境においてルートCAのローテーションを行った際の手順です。手順はReferencesに記載したチュートリアルを参考にしていますので、必要に応じてそちらのチュートリアルもご参照ください。

Prerequisites

  • Ubuntu 22.04 LTS
  • Vault Enterprise HSM v1.11以降のバージョン
  • softhsm2 パッケージ
  • opensc パッケージ (pkcs11-tool CLI実行用にインストール)

Configure SoftHSM2

vault-enterprise-hsmパッケージをUbuntu上にインストールし、systemdでVaultが起動する様に設定しています。

/etc/systemd/system/vault.service
[Unit]
Description="HashiCorp Vault - A tool for managing secrets"
Documentation=https://developer.hashicorp.com/vault/docs
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/vault.d/config.hcl
StartLimitIntervalSec=60
StartLimitBurst=3
 
[Service]
Type=notify
EnvironmentFile=/etc/vault.d/vault.env
User=vault
Group=vault
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep-caps
AmbientCapabilities=CAP_IPC_LOCK
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/bin/vault server -config=/etc/vault.d/config.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGINT
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
LimitNOFILE=65536
LimitMEMLOCK=infinity
LimitCORE=0
 
[Install]
WantedBy=multi-user.target

Vaultの実行ユーザーであるvaultがPKCS#11のライブラリにアクセスする必要があるため、以下のパーミッション設定を入れています。

sudo chown root:vault /etc/softhsm
sudo chmod 750 /etc/softhsm

sudo chown root:vault /etc/softhsm/softhsm2.conf
sudo chmod 640 /etc/softhsm/softhsm2.conf

sudo chown -R vault:vault /var/lib/softhsm
sudo chmod -R 750 /var/lib/softhsm

また、Vaultサービスが読み込む環境変数ファイル/etc/vault.d/vault.envに環境変数SOFTHSM2_CONFを設定し、SoftHSM2の設定ファイルにVaultサービスがアクセスできる様にしています。

echo "SOFTHSM2_CONF=/etc/softhsm/softhsm2.conf" >> /etc/vault.d/vault.env

次のステップに進む前に、vaultユーザーがPKCS#11ライブラリにアクセス出来るかどうか確認しておきます。

sudo -u vault pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so -I
出力例
Cryptoki version 2.40
Manufacturer     SoftHSM
Library          Implementation of PKCS11 (ver 2.6)
Using slot 0 with a present token (0x0)

PKCS#11トークンの初期化をrootユーザーで実行します。

softhsm2-util --init-token --free --so-pin=supersecret --pin=prettysecret --label="test-pki-managed-key" | tee /home/ubuntu/softhsm2-init.out

以下の様なレスポンスが返ると思います。ここで出力されたslot番号(以下の例では、1065878452)が後ほど必要になるため、確認しておきます。

出力例
Slot 0 has a free/uninitialized token.
The token has been initialized and is reassigned to slot 1065878452

Configure Vault

VaultクラスタはVaultサーバ1台で構成しています。Vaultの構成ファイルは以下の様に設定しています。

/etc/vault.d/config.hcl
api_addr     = "http://xx.xxx.x.xx:8200"
cluster_addr = "http://xx.xxx.x.xx:8201"

ui = true

license_path = "/etc/vault.d/vault.hclic"

listener "tcp" {
  address         = "0.0.0.0:8200"
  cluster_address = "0.0.0.0:8201"
  tls_disable     = true
}

storage "raft" {
  path    = "/var/lib/vault"
  node_id = "vault1"
}

telemetry {
  disable_hostname          = true
  prometheus_retention_time = "12h"
}

kms_library "pkcs11" {
  name    = "myhsm"
  library = "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so"
}

マネージドキーの機能を利用するために、kms_libraryスタンザを用いて設定しています。

この環境で利用しているVaultのバイナリは、Vault Enterprise HSMバイナリになります。

$ vault version
Vault v1.17.2+ent.hsm (023471f83b7698e89424be66ed2ac8e5dad5fecc), built 2024-07-05T15:19:27Z (cgo)

Vaultサービスを起動します。

sudo systemctl start vault

問題なくVaultが起動したら、Vaultの初期化を行います。

export VAULT_ADDR=http://127.0.0.1:8200
vault operator init -key-shares=1 -key-threshold=1 | tee init.out

初期化の際に出力される初期ルートトークンや、Unsealキーを環境変数に設定し、VaultのバックエンドストレージをUnsealします。

export ROOT_TOKEN=
export UNSEAL_KEY=
export VAULT_TOKEN=$ROOT_TOKEN
vault operator unseal $UNSEAL_KEY

Unseal処理が完了したら、Vaultのステータスを確認します。InitializedtrueSealedfalseになっている事を確認し、次に進みます。

vault status
出力例
Key                     Value
---                     -----
Seal Type               shamir
Initialized             true
Sealed                  false
Total Shares            1
Threshold               1
Version                 1.17.2+ent.hsm
Build Date              2024-07-05T15:19:27Z
Storage Type            raft
Cluster Name            vault-cluster-01cfe84c
Cluster ID              2c8a32cb-fd26-bf4d-6342-6f99d7e4877b
HA Enabled              true
HA Cluster              https://xx.xxx.x.xx:8201
HA Mode                 active
Active Since            2024-07-10T03:04:07.068751551Z
Raft Committed Index    87
Raft Applied Index      87
Last WAL                30

Configure a Managed Key

次に、マネージドキーの設定をしていきます。APIドキュメントのCreate/Update managed keyに設定時に指定出来るパラメーターが記載されています。今回はマネージドキーとして利用するのは、PKCS#11のSoftHSMなので、PKCS#11 backend parametersを参照して設定してきます。

vault write sys/managed-keys/pkcs11/pki-managed-key \
  library=myhsm slot=1065878452 pin=prettysecret \
  key_label=test-pki-managed-key \
  allow_store_key=false \
  allow_generate_key=true \
  mechanism=0x0001 key_bits=4096 \
  any_mount=false
出力例
Success! Data written to: sys/managed-keys/pkcs11/pki-managed-key

Vaultの設定ファイルで構成したkms_libraryスタンザを参照するlibrary、PKCS#11トークンの初期化で出力されたslot、初期化時に指定したpinを指定しています。
allow_generate_keyは、VaultがバックエンドのHSMに鍵の生成を要求してもよいことを指定しています。

設定したマネージドキーの設定を確認します。

vault read sys/managed-keys/pkcs11/pki-managed-key
出力例
Key                   Value
---                   -----
UUID                  b367b22a-3d7e-2f55-2fec-6b08979afea4
allow_generate_key    true
allow_replace_key     false
allow_store_key       false
any_mount             false
key_bits              4096
key_label             test-pki-managed-key
library               myhsm
mechanism             1
name                  pki-managed-key
pin                   redacted
slot                  1065878452
type                  pkcs11
usages                [3 4]

APIドキュメントTest sign with a managed keyに記載がある様に、このエンドポイントを利用することで、ランダムに生成されたデータに署名して検証を行い、マネージドキーが正しく機能することを検証できます。

vault write -force /sys/managed-keys/pkcs11/pki-managed-key/test/sign
出力例
Success! Data written to: sys/managed-keys/pkcs11/pki-managed-key/test/sign

上記の様に出力されれば、マネージドキーの設定が上手く行われています。続いて、PKIシークレットエンジンをマネージドキーを利用する形で構成していきます。

Configure root CA

マネージドキーを用いたPKIシークレットエンジンの構成をVaultプロバイダを利用して、Terraformで設定していきます。

terraform.tf
terraform {
  required_providers {
    vault = {
      source  = "hashicorp/vault"
      version = "~> 4.3.0"
    }
  }

  required_version = "~> 1.9"
}
main.tf
provider "vault" {}

resource "vault_mount" "pki" {
  path                      = "pki"
  type                      = "pki"
  description               = "root ca pki mount with managed-keys"
  default_lease_ttl_seconds = 2592000   #30days
  max_lease_ttl_seconds     = 315360000 #10years
  allowed_managed_keys      = ["pki-managed-key"]
}

resource "vault_pki_secret_backend_root_cert" "root_2023" {
  backend          = vault_mount.pki.path
  type             = "kms"
  managed_key_name = "pki-managed-key"
  common_name      = "example.com"
  ttl              = 315360000 #10years
  issuer_name      = "root-2023"
}

resource "local_file" "root_2023_cert" {
  content  = vault_pki_secret_backend_root_cert.root_2023.certificate
  filename = "root_2023_ca.crt"
}

resource "vault_pki_secret_backend_issuer" "root_2023" {
  backend                        = vault_mount.pki.path
  issuer_ref                     = vault_pki_secret_backend_root_cert.root_2023.issuer_id
  issuer_name                    = vault_pki_secret_backend_root_cert.root_2023.issuer_name
  revocation_signature_algorithm = "SHA256WithRSA"
}

resource "vault_pki_secret_backend_config_urls" "config-urls" {
  backend                 = vault_mount.pki.path
  issuing_certificates    = ["http://127.0.0.1:8200/v1/pki/ca"]
  crl_distribution_points = ["http://127.0.0.1:8200/v1/pki/crl"]
}

outputs.tf
output "vault_pki_secret_backend_root_cert_root_2023" {
  value = vault_pki_secret_backend_root_cert.root_2023.certificate
}

vault_mout.pkiリソースブロックの中で、allowed_managed_keysパラメーターで設定したマネージドキーpki-managed-keyを指定しています。

vault_pki_secret_backend_root_cert.root_2023リソースブロックで、ルート証明書を生成していますが、ここではtypeとしてkmsmanaged_key_nameとしてpki-managed-keyを指定し、設定したマネージドキーを利用して、ルート証明書を生成する様にしています。

他は一般的なPKIシークレットエンジンと同様の設定をしています。Terraformの処理を実行して、コードをVaultクラスタに反映させます。

terraform init
terraform plan
terraform apply -auto-approve

PKIシークレットエンジンのマウントポイントpki/が作成されている事が確認できます。

$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_4b6d8ae7    per-token private secret storage
identity/     identity     identity_618d63fa     identity store
pki/          pki          pki_55750f24          root ca pki mount with managed-keys
sys/          system       system_66a6f569       system endpoints used for control, policy and debugging

続いて、中間CAも同じVaultクラスタ内に設定していきます。

Configure intermediate CA

上記ではPKIシークレットエンジンを利用して、ルートCAのみをVault内に作成しました。このルートCAを利用した中間CAもVault内に作成します。
main.tfに以下の内容を追加し、Terraformの処理を実行し、中間CAから証明書を発行出来る様にします。

main.tf
##################################
## intermediate CA
##################################

resource "vault_mount" "pki_int" {
  path                      = "pki_int"
  type                      = "pki"
  description               = "intermediate ca pki mount with managed-keys"
  default_lease_ttl_seconds = 2592000   #30days
  max_lease_ttl_seconds     = 157680000 #5years
  allowed_managed_keys      = ["pki-managed-key"]
}

resource "vault_pki_secret_backend_intermediate_cert_request" "csr_request" {
  backend          = vault_mount.pki_int.path
  type             = "kms"
  managed_key_name = "pki-managed-key"
  common_name      = "example.com Intermediate Authority"
}

resource "local_file" "csr_request_cert" {
  content  = vault_pki_secret_backend_intermediate_cert_request.csr_request.csr
  filename = "pki_intermediate.csr"
}

resource "vault_pki_secret_backend_root_sign_intermediate" "intermediate" {
  backend     = vault_mount.pki.path
  common_name = "new_intermediate"
  csr         = vault_pki_secret_backend_intermediate_cert_request.csr_request.csr
  format      = "pem_bundle"
  ttl         = 15552000 #180days
  issuer_ref  = vault_pki_secret_backend_root_cert.root_2023.issuer_id
}

resource "local_file" "intermediate_ca_cert" {
  content  = vault_pki_secret_backend_root_sign_intermediate.intermediate.certificate
  filename = "intermediate.cert.pem"
}

resource "vault_pki_secret_backend_intermediate_set_signed" "intermediate" {
  backend     = vault_mount.pki_int.path
  certificate = vault_pki_secret_backend_root_sign_intermediate.intermediate.certificate
}

resource "vault_pki_secret_backend_issuer" "intermediate" {
  backend     = vault_mount.pki_int.path
  issuer_ref  = vault_pki_secret_backend_intermediate_set_signed.intermediate.imported_issuers[0]
  issuer_name = "example-dot-com-intermediate"
}

resource "vault_pki_secret_backend_role" "intermediate_role" {
  backend          = vault_mount.pki_int.path
  issuer_ref       = vault_pki_secret_backend_issuer.intermediate.issuer_ref
  name             = "example-dot-com"
  ttl              = 86400
  max_ttl          = 2592000
  allow_ip_sans    = true
  key_type         = "rsa"
  key_bits         = 4096
  allowed_domains  = ["example.com"]
  allow_subdomains = true
}

以下のAPIドキュメントに記載があるように、中間CAの生成でもマネージドキーが利用出来るので、その様な構成にしています。
https://developer.hashicorp.com/vault/api-docs/secret/pki#managed-keys

The Generate Root and Generate Intermediate API calls can leverage the Managed Keys feature, delegating operations that require private key material to an external system.

ルートCAの時と同様に、vault_mout.pki_intリソースブロックの中で、allowed_managed_keysパラメーターで設定したマネージドキーpki-managed-keyを指定しています。

vault_pki_secret_backend_intermediate_cert_request.csr_requestリソースブロックで、秘密鍵と署名用のCSRを作成していますが、ここでもtypeとしてkmsmanaged_key_nameとしてpki-managed-keyを指定し、設定したマネージドキーを利用して、秘密鍵を生成する様にしています。

ルートCAを設定した時と同様に、Terraformの処理を実行し、変更を反映させます。

terraform init
terraform plan
terraform apply -auto-approve

PKIシークレットエンジンのマウントポイントが追加されている事が確認できると思います。pki_int/のマウントポイントはPKIシークレットエンジンを用いて中間CAとして定義しています、また、pki_int/のルートCAとしてはpki/マウントポイントを利用するという構成になっています。

$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_4b6d8ae7    per-token private secret storage
identity/     identity     identity_618d63fa     identity store
pki/          pki          pki_55750f24          root ca pki mount with managed-keys
pki_int/      pki          pki_4226fbf5          intermediate ca pki mount with managed-keys
sys/          system       system_66a6f569       system endpoints used for control, policy and debugging

中間CAに対して作成したPKIロールexample-dot-comを利用して、証明書を発行してみます。

vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
出力例
Key                 Value
---                 -----
ca_chain            [-----BEGIN CERTIFICATE-----
MIIFlDCCA3ygAwIBAgIUOTUtY9WdD1jeJVOOBGccEtaXZnkwDQYJKoZIhvcNAQEL
...
EyflgKCWxvsF/KH0geCFV4BA+hSMb/QbZ9MC2YLPnK2HgwkssuWf7w==
-----END CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFpzCCA4+gAwIBAgIUE6d6pVm4GjO60td+6Q/xdpCO3qUwDQYJKoZIhvcNAQEL
...
XE8VA2lZUGFykdLi7hhPhmHjJdOmmeAqgLyHLuEMmnX6OMvDVapGeLfBg+Rfc8mZ
HeykB0AJlG35c4k=
-----END CERTIFICATE-----]
certificate         -----BEGIN CERTIFICATE-----
MIIFVDCCAzygAwIBAgIUIb+Cva1H9g9qYHqmX2BVnF9qN5kwDQYJKoZIhvcNAQEL
...
zBHXPqiK/a+Zykflk7U6uTaIC3AKzeUS
-----END CERTIFICATE-----
expiration          1720589055
issuing_ca          -----BEGIN CERTIFICATE-----
MIIFlDCCA3ygAwIBAgIUOTUtY9WdD1jeJVOOBGccEtaXZnkwDQYJKoZIhvcNAQEL
...
EyflgKCWxvsF/KH0geCFV4BA+hSMb/QbZ9MC2YLPnK2HgwkssuWf7w==
-----END CERTIFICATE-----
private_key         -----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAwf4u4oRigPLSgnjRRKhNVUMe+XdmQTe8v13DO4HAb+6g6Fw8
...
F4H2z2Reia7RIsh7s1ay0soq5lZz3TrZ7ER5TTZz9Ca4uKGCUUbYb7NpiHg=
-----END RSA PRIVATE KEY-----
private_key_type    rsa
serial_number       serial_number       2b:ca:ca:60:09:f7:c4:67:d2:4e:35:9a:65:f8:8e:88:8d:3d:54:2e

ルートCA、中間CAをVault内に設定する事が出来ました。続いて、ルートCAのローテーションを行っていきたいと思います。

Rotate root CA

v1.11以前では、同じPKIシークレットエンジンのマウントポイントで複数のルートCAを有効にすることができませんでした。
v1.11から、PKIシークレットエンジンに、同じシークレットエンジンのマウントポイントに複数のルートCAを含めるマルチ発行者機能を含む新機能が導入されました。これにより、以前より簡単にルートCAのローテーションが可能になりました。
https://developer.hashicorp.com/vault/docs/release-notes/1.11.0#improved-ca-rotation

マネージドキーを利用している環境でも問題なく利用出来るかを確認してみます。

ここからはTerraformではなく、vaultCLIを利用して設定していきます。ローテーション用のエンドポイントを利用して、既存のPKIシークレットエンジンのマウントポイントpki/に新しいルートCAの発行者を作成します。

利用するAPIエンドポイントは以下のドキュメントに記載がある/pki/root/rotate/:typeになります。typeとしてkmsを指定する事が出来るので、マネージドキーを指定して作成します。
https://developer.hashicorp.com/vault/api-docs/secret/pki#generate-root

vault write pki/root/rotate/kms managed_key_name="pki-managed-key" common_name="example.com" issuer_name="root-2024"
出力例
Key              Value
---              -----
certificate      -----BEGIN CERTIFICATE-----
MIIFpzCCA4+gAwIBAgIUOpcrMom4nt1vnwVVBnMDxuSgrkkwDQYJKoZIhvcNAQEL
...
P9yO9lK5artZUsg=
-----END CERTIFICATE-----
expiration       1723177470
issuer_id        4c9d2d43-7392-0ce7-f55e-00c852941a75
issuer_name      root-2024
issuing_ca       -----BEGIN CERTIFICATE-----
MIIFpzCCA4+gAwIBAgIUOpcrMom4nt1vnwVVBnMDxuSgrkkwDQYJKoZIhvcNAQEL
...
P9yO9lK5artZUsg=
-----END CERTIFICATE-----
key_id           25d3c4b0-7bd9-9701-9a25-17466d2abc41
key_name         n/a
serial_number    3a:97:2b:32:89:b8:9e:dd:6f:9f:05:55:06:73:03:c6:e4:a0:ae:49

ルートCAのマウントポイントとして定義しているpki/で、複数のルートCA発行者が作成されている事を確認します。

$ vault list pki/issuers
Keys
----
4c9d2d43-7392-0ce7-f55e-00c852941a75
76254618-5997-53fa-ab69-17e772957c39

それぞれのissuer_nameが違うことから、別のルートCA発行者が同一のルートCAのマウントポイントpki/で定義されている事が分かると思います。現実点では、デフォルト設定されているroot-2023のルートCA発行者を介した証明書が発行されます。

issuer_name=root-2023 -> 76254618-5997-53fa-ab69-17e772957c39
$ vault read pki/issuer/76254618-5997-53fa-ab69-17e772957c39 | tail -11
crl_distribution_points           []
issuer_id                         76254618-5997-53fa-ab69-17e772957c39
issuer_name                       root-2023
issuing_certificates              []
key_id                            25d3c4b0-7bd9-9701-9a25-17466d2abc41
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    SHA256WithRSA
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only
issuer_name=root-2024 -> 4c9d2d43-7392-0ce7-f55e-00c852941a75
$ vault read pki/issuer/4c9d2d43-7392-0ce7-f55e-00c852941a75 | tail -11
crl_distribution_points           []
issuer_id                         4c9d2d43-7392-0ce7-f55e-00c852941a75
issuer_name                       root-2024
issuing_certificates              []
key_id                            25d3c4b0-7bd9-9701-9a25-17466d2abc41
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    SHA256WithRSA
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only

PKIロールを設定する際に、issuerを明示的に指定する事が可能です。ルートCAをローテーションした後のテスト用に、pki/マウントポイントに対してissuerroot-2023に指定して、PKIロール2023-serverを作成しておきます。

vault write pki/roles/2023-server max_ttl=2592000 allow_any_name=true issuer_ref=root-2023
出力例
Key                                   Value
---                                   -----
allow_any_name                        true
allow_bare_domains                    false
allow_glob_domains                    false
allow_ip_sans                         true
allow_localhost                       true
allow_subdomains                      false
allow_token_displayname               false
allow_wildcard_certificates           true
allowed_domains                       []
allowed_domains_template              false
allowed_other_sans                    []
allowed_serial_numbers                []
allowed_uri_sans                      []
allowed_uri_sans_template             false
allowed_user_ids                      []
basic_constraints_valid_for_non_ca    false
client_flag                           true
cn_validations                        [email hostname]
code_signing_flag                     false
country                               []
email_protection_flag                 false
enforce_hostnames                     true
ext_key_usage                         []
ext_key_usage_oids                    []
generate_lease                        false
issuer_ref                            root-2023
key_bits                              2048
key_type                              rsa
key_usage                             [DigitalSignature KeyAgreement KeyEncipherment]
locality                              []
max_ttl                               720h
no_store                              false
no_store_metadata                     false
not_after                             n/a
not_before_duration                   30s
organization                          []
ou                                    []
policy_identifiers                    []
postal_code                           []
province                              []
require_cn                            true
server_flag                           true
signature_bits                        256
street_address                        []
ttl                                   0s
use_csr_common_name                   true
use_csr_sans                          true
use_pss                               false

issuer_refroot-2023となっている事が確認出来ると思います。

証明書を発行する際は以下の様なコマンドを実行します。

vault write pki/issue/2023-server common_name="test.example.com" ttl="24h"

Create a cross-signed intermediate

ルートCAのローテーションに対しては、create a root bridge CAcreate a cross-signed intermediate、どちらかを選択して対応する形になりますが、ここでは後者のcreate a cross-signed intermediateの方法を試していきます。

まずは、相互署名用のCSRを作成します。中間CAに対して作業を行いますので、PKIシークレットエンジンのマウントポイントはpki_int/を使用します。

vault write -format=json pki_int/intermediate/cross-sign \
  common_name="example.com Intermediate Authority" \
  key_ref="$(vault read pki_int/issuer/$(vault read -field=default pki_int/config/issuers) \
  | grep -i key_id | awk '{print $2}')" \
  | jq -r '.data.csr' \
  | tee cross-signed-intermediate.csr
出力例
-----BEGIN CERTIFICATE REQUEST-----
MIIEcjCCAloCAQAwLTErMCkGA1UEAxMiZXhhbXBsZS5jb20gSW50ZXJtZWRpYXRl
...
Uj0vN7DdOu2glNkpAAWLL2tAHyVYN/cHcgoGAnf5SIJH0QnWNks=
-----END CERTIFICATE REQUEST-----

次に、新しいルートCA発行者root-2024を利用して、先ほど生成したCSRcross-signed-intermediate.csrに署名します。ルートCAで署名を行いますので、PKIシークレットエンジンのマウントポイントは、pki/になります。

vault write -format=json pki/issuer/root-2024/sign-intermediate \
  common_name="example.com Intermediate Authority" \
  csr=@cross-signed-intermediate.csr \
  | jq -r '.data.certificate' | tee cross-signed-intermediate.crt
出力例
-----BEGIN CERTIFICATE-----
MIIFpjCCA46gAwIBAgIUA5kLiqOI8QfwTFP6tt4K3h6GV+0wDQYJKoZIhvcNAQEL
...
AXyKudEGKZFzbQ==
-----END CERTIFICATE-----

相互署名された証明書cross-signed-intermediate.crtを、中間CAのマウントポイントpki_int/に対してインポートします。

vault write pki_int/intermediate/set-signed certificate=@cross-signed-intermediate.crt
出力例
Key                 Value
---                 -----
existing_issuers    <nil>
existing_keys       <nil>
imported_issuers    [c38781de-162b-cfad-6da2-ebebe97d9604]
imported_keys       <nil>
mapping             map[c38781de-162b-cfad-6da2-ebebe97d9604:77cef66b-8ee0-358e-ec91-5c7ce289cf68]

中間CAにインポートした相互署名された証明書の発行者(imported_issuers = c38781de-162b-cfad-6da2-ebebe97d9604)の発行者名を、xc-example-dot-com-intermediateと設定しておきます。

vault write pki_int/issuer/c38781de-162b-cfad-6da2-ebebe97d9604 issuer_name=xc-example-dot-com-intermediate
出力例
Key                               Value
---                               -----
ca_chain                          [-----BEGIN CERTIFICATE-----
MIIFpjCCA46gAwIBAgIUA5kLiqOI8QfwTFP6tt4K3h6GV+0wDQYJKoZIhvcNAQEL
...
AXyKudEGKZFzbQ==
-----END CERTIFICATE-----
 -----BEGIN CERTIFICATE-----
MIIFpzCCA4+gAwIBAgIUI0S0LGnYhhQH6gJ9WO7c46L3GIYwDQYJKoZIhvcNAQEL
...
s57ytXUtfUn/0GY=
-----END CERTIFICATE-----
]
certificate                       -----BEGIN CERTIFICATE-----
MIIFpjCCA46gAwIBAgIUA5kLiqOI8QfwTFP6tt4K3h6GV+0wDQYJKoZIhvcNAQEL
...
AXyKudEGKZFzbQ==
-----END CERTIFICATE-----
crl_distribution_points           []
issuer_id                         c38781de-162b-cfad-6da2-ebebe97d9604
issuer_name                       xc-example-dot-com-intermediate
issuing_certificates              []
key_id                            77cef66b-8ee0-358e-ec91-5c7ce289cf68
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    n/a
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only

Set default issuer in root CA

中間CA側の作業が全て完了した上で、ルートCAでデフォルトの発行者を変更していきます。

ルートCAの現在のデフォルト発行者を確認します。

vault read pki/issuer/default | tail -11
出力例
crl_distribution_points           []
issuer_id                         76254618-5997-53fa-ab69-17e772957c39
issuer_name                       root-2023
issuing_certificates              []
key_id                            25d3c4b0-7bd9-9701-9a25-17466d2abc41
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    SHA256WithRSA
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only

/pki/root/replaceエンドポイントを利用して、デフォルトの発行者を更新する事が出来ます。
https://developer.hashicorp.com/vault/api-docs/secret/pki#set-issuers-configuration

vault write pki/root/replace default=root-2024
出力例
Key                              Value
---                              -----
default                          4c9d2d43-7392-0ce7-f55e-00c852941a75
default_follows_latest_issuer    false

変更されたか確認してみます。ルートCAのデフォルトのissuer_nameroot-2024に変更されている事が分かります。

vault read pki/issuer/default | tail -11
出力例
crl_distribution_points           []
issuer_id                         4c9d2d43-7392-0ce7-f55e-00c852941a75
issuer_name                       root-2024
issuing_certificates              []
key_id                            25d3c4b0-7bd9-9701-9a25-17466d2abc41
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    SHA256WithRSA
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only

Sunset old root CA

続いて、古いルートCAの発行者であるroot-2023を、CRLに署名したり、証明書を失効させたりする機能は保持したまま、証明書を発行する機能を削除していきます。

vault write pki/issuer/root-2023 issuer_name="root-2023" usage=read-only,crl-signing | tail -n 11
出力例
crl_distribution_points           []
issuer_id                         76254618-5997-53fa-ab69-17e772957c39
issuer_name                       root-2023
issuing_certificates              []
key_id                            25d3c4b0-7bd9-9701-9a25-17466d2abc41
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    n/a
revoked                           false
usage                             crl-signing,read-only

usageからissuing-certificatesが削除され、crl-signingread-onlyだけが古いルートCAの発行者root-2023で使用可能な様に変更しました。これは、今後古いルートCAの発行者であるroot-2023を介して、証明書が発行できないことを意味します。

作成しておいたルートCAの発行者root-2023を用いたPKIロール2023-serverを利用して、証明書を発行しようとしてみます。以下の様にルートCAの発行者にissuing-certificatesの機能がないため、証明書を発行出来なくなっているのが分かります。

vault write pki/issue/2023-server common_name="test.example.com" ttl="24h"
出力例
Error writing data to pki/issue/2023-server: Error making API request.

URL: PUT http://127.0.0.1:8200/v1/pki/issue/2023-server
Code: 500. Errors:

* 1 error occurred:
	* error fetching CA certificate: error while attempting to use issuer 76254618-5997-53fa-ab69-17e772957c39: requested usage issuing-certificates for issuer [id:76254618-5997-53fa-ab69-17e772957c39 / name:root-2023] but only had usage crl-signing,read-only

Set default issuer in intermediate CA

中間CA側の証明書の発行者を確認してみます。デフォルトの発行者は、example-dot-com-intermediateとなっている事が分かります。

$ vault read pki_int/issuer/default |tail -11
crl_distribution_points           []
issuer_id                         2cc72790-a41f-1652-9bdf-72737eaf4cb0
issuer_name                       example-dot-com-intermediate
issuing_certificates              []
key_id                            77cef66b-8ee0-358e-ec91-5c7ce289cf68
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    n/a
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only

Create a cross-signed intermediateで中間CAにインポートした相互署名された証明書の発行者(xc-example-dot-com-intermediate)の情報を改めて確認しておきます。

$ vault read pki_int/issuer/c38781de-162b-cfad-6da2-ebebe97d9604 |tail -11
crl_distribution_points           []
issuer_id                         c38781de-162b-cfad-6da2-ebebe97d9604
issuer_name                       xc-example-dot-com-intermediate
issuing_certificates              []
key_id                            77cef66b-8ee0-358e-ec91-5c7ce289cf68
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    n/a
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only

ルートCAで行った時と同様に、デフォルトの発行者を相互署名されているxc-example-dot-com-intermediateに変更します。

vault write pki_int/root/replace default=xc-example-dot-com-intermediate
出力例
Key                              Value
---                              -----
default                          c38781de-162b-cfad-6da2-ebebe97d9604
default_follows_latest_issuer    false

中間CAのデフォルト発行者が変更された事を確認します。

$ vault read pki_int/issuer/default |tail -11
crl_distribution_points           []
issuer_id                         c38781de-162b-cfad-6da2-ebebe97d9604
issuer_name                       xc-example-dot-com-intermediate
issuing_certificates              []
key_id                            77cef66b-8ee0-358e-ec91-5c7ce289cf68
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    n/a
revoked                           false
usage                             crl-signing,issuing-certificates,ocsp-signing,read-only

中間CAに設定したPKIロールexample-dot-comの設定を確認してみます。issuer_ref2cc72790-a41f-1652-9bdf-72737eaf4cb0となっており、発行者はexample-dot-com-intermediateとなっている事が分かります。

vault read pki_int/roles/example-dot-com
出力例
Key                                   Value
---                                   -----
allow_any_name                        false
allow_bare_domains                    false
allow_glob_domains                    false
allow_ip_sans                         true
allow_localhost                       true
allow_subdomains                      true
allow_token_displayname               false
allow_wildcard_certificates           true
allowed_domains                       [example.com]
allowed_domains_template              false
allowed_other_sans                    []
allowed_serial_numbers                []
allowed_uri_sans                      []
allowed_uri_sans_template             false
allowed_user_ids                      []
basic_constraints_valid_for_non_ca    false
client_flag                           true
cn_validations                        [email hostname]
code_signing_flag                     false
country                               []
email_protection_flag                 false
enforce_hostnames                     true
ext_key_usage                         []
ext_key_usage_oids                    []
generate_lease                        false
issuer_ref                            2cc72790-a41f-1652-9bdf-72737eaf4cb0
key_bits                              4096
key_type                              rsa
key_usage                             [DigitalSignature KeyAgreement KeyEncipherment]
locality                              []
max_ttl                               720h
no_store                              false
no_store_metadata                     false
not_after                             n/a
not_before_duration                   30s
organization                          []
ou                                    []
policy_identifiers                    []
postal_code                           []
province                              []
require_cn                            true
server_flag                           true
signature_bits                        256
street_address                        []
ttl                                   24h
use_csr_common_name                   true
use_csr_sans                          true
use_pss                               false

問題なく証明書を発行出来る事を確認します。

vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
出力例
Key                 Value
---                 -----
ca_chain            [-----BEGIN CERTIFICATE-----
MIIFlDCCA3ygAwIBAgIUW+aRTRuWs21kn13FS44Q+eF7wHMwDQYJKoZIhvcNAQEL
...
K0V0Tfh8Ipmw6SQCnxA3xFLAa3RQcHoiaOAP3+q1boEyWtBto+CxOA==
-----END CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFpzCCA4+gAwIBAgIUI0S0LGnYhhQH6gJ9WO7c46L3GIYwDQYJKoZIhvcNAQEL
...
s57ytXUtfUn/0GY=
-----END CERTIFICATE-----]
certificate         -----BEGIN CERTIFICATE-----
MIIFVDCCAzygAwIBAgIUMu8JF4p/1zsUXALmgpSy4FhujvowDQYJKoZIhvcNAQEL
...
Fo/cHeXHG523zr7AWH3Sb0nbmzt7Aj3p
-----END CERTIFICATE-----
expiration          1720773506
issuing_ca          -----BEGIN CERTIFICATE-----
MIIFlDCCA3ygAwIBAgIUW+aRTRuWs21kn13FS44Q+eF7wHMwDQYJKoZIhvcNAQEL
...
K0V0Tfh8Ipmw6SQCnxA3xFLAa3RQcHoiaOAP3+q1boEyWtBto+CxOA==
-----END CERTIFICATE-----
private_key         -----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAvYqT5tlnChIsQVz8zdmPgQ+PvtzVIJUsZOqWgqpkQuxu0EU3
...
3XFdAu8h+GjHMtex6SDc8KGvHOs6Bxp3pJs6t3GDnmepXybjTIbF3Mn7DXA=
-----END RSA PRIVATE KEY-----
private_key_type    rsa
serial_number       32:ef:09:17:8a:7f:d7:3b:14:5c:02:e6:82:94:b2:e0:58:6e:8e:fa

Sunset old root CAで実施したのと同様に、中間CAの発行者example-dot-com-intermediateから証明書が発行できない様に設定を変更します。

vault write pki_int/issuer/2cc72790-a41f-1652-9bdf-72737eaf4cb0 issuer_name="example-dot-com-intermediate" usage=read-only,crl-signing | tail -n 11
出力例
crl_distribution_points           []
issuer_id                         2cc72790-a41f-1652-9bdf-72737eaf4cb0
issuer_name                       example-dot-com-intermediate
issuing_certificates              []
key_id                            77cef66b-8ee0-358e-ec91-5c7ce289cf68
leaf_not_after_behavior           err
manual_chain                      <nil>
ocsp_servers                      []
revocation_signature_algorithm    n/a
revoked                           false
usage                             crl-signing,read-only

先ほどと同様に証明書の発行をPKIロールexample-dot-comで試みてみますが、以下の様に発行者example-dot-com-intermediateを介して、新たな証明書は発行出来ないため、エラーが返ります。

vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
出力例
Error writing data to pki_int/issue/example-dot-com: Error making API request.

URL: PUT http://127.0.0.1:8200/v1/pki_int/issue/example-dot-com
Code: 500. Errors:

* 1 error occurred:
	* error fetching CA certificate: error while attempting to use issuer 2cc72790-a41f-1652-9bdf-72737eaf4cb0: requested usage issuing-certificates for issuer [id:2cc72790-a41f-1652-9bdf-72737eaf4cb0 / name:example-dot-com-intermediate] but only had usage crl-signing,read-only

PKIロールexample-dot-comを更新し、デフォルト発行者になっているxc-example-dot-com-intermediateを発行者に変更します。

vault write pki_int/roles/example-dot-com ttl=86400 max_ttl=2592000 allow_ip_sans=true key_type=rsa key_bits=4096 allowed_domains="example.com" allow_subdomains=true
出力例
Key                                   Value
---                                   -----
allow_any_name                        false
allow_bare_domains                    false
allow_glob_domains                    false
allow_ip_sans                         true
allow_localhost                       true
allow_subdomains                      true
allow_token_displayname               false
allow_wildcard_certificates           true
allowed_domains                       [example.com]
allowed_domains_template              false
allowed_other_sans                    []
allowed_serial_numbers                []
allowed_uri_sans                      []
allowed_uri_sans_template             false
allowed_user_ids                      []
basic_constraints_valid_for_non_ca    false
client_flag                           true
cn_validations                        [email hostname]
code_signing_flag                     false
country                               []
email_protection_flag                 false
enforce_hostnames                     true
ext_key_usage                         []
ext_key_usage_oids                    []
generate_lease                        false
issuer_ref                            default
key_bits                              4096
key_type                              rsa
key_usage                             [DigitalSignature KeyAgreement KeyEncipherment]
locality                              []
max_ttl                               720h
no_store                              false
no_store_metadata                     false
not_after                             n/a
not_before_duration                   30s
organization                          []
ou                                    []
policy_identifiers                    []
postal_code                           []
province                              []
require_cn                            true
server_flag                           true
signature_bits                        256
street_address                        []
ttl                                   24h
use_csr_common_name                   true
use_csr_sans                          true
use_pss                               false

issuer_refが変更され、default(発行者名xc-example-dot-com-intermediate)に更新されている事が分かります。

この状態で再度PKIロールexample-dot-comを用いて、証明書を発行してみます。今回は問題なく証明書を発行する事が出来ています。

vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
出力例
Key                 Value
---                 -----
ca_chain            [-----BEGIN CERTIFICATE-----
MIIFpjCCA46gAwIBAgIUA5kLiqOI8QfwTFP6tt4K3h6GV+0wDQYJKoZIhvcNAQEL
...
AXyKudEGKZFzbQ==
-----END CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIFpzCCA4+gAwIBAgIUI0S0LGnYhhQH6gJ9WO7c46L3GIYwDQYJKoZIhvcNAQEL
...
s57ytXUtfUn/0GY=
-----END CERTIFICATE-----]
certificate         -----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIUIOKWoLpJTI5vgmpXRj+xBm6IxXowDQYJKoZIhvcNAQEL
...
BdOKOqKfx85HxeyYkTPiSSofA1LCIXq7hImXFAyW3n0xQqKebnWhbXj8
-----END CERTIFICATE-----
expiration          1720774352
issuing_ca          -----BEGIN CERTIFICATE-----
MIIFpjCCA46gAwIBAgIUA5kLiqOI8QfwTFP6tt4K3h6GV+0wDQYJKoZIhvcNAQEL
...
AXyKudEGKZFzbQ==
-----END CERTIFICATE-----
private_key         -----BEGIN RSA PRIVATE KEY-----
MIIJJwIBAAKCAgEA6l3BhWBvQHZE+TwpSioPa0OMzoLVskb89Xot8ddQu4wn86yp
...
vUcXi7hNGvP3PhZ7ypjyKIlWz07GHIl/4dm0OvVO+gU6zHsSJpzZnVgHAA==
-----END RSA PRIVATE KEY-----
private_key_type    rsa
serial_number       20:e2:96:a0:ba:49:4c:8e:6f:82:6a:57:46:3f:b1:06:6e:88:c5:7a

PKIロールを作成する際、issuer_refdefaultと指定しておけば、デフォルトの発行者を変更した際に、PKIロール側を変更する必要は無いと思いますので、実際に運用される際はその様に設定しておいた方が良いかと思います。

マネージドキーの機能を利用して、PKIシークレットエンジンを設定し、その環境下でのルートCAのローテーション等を行ってみました。参考になりそうな部分があれば、ご活用下さい!

References

Discussion