Vault PKI와 Consul Template으로 mTLS 인증서 자동 생성

Vault PKI와 Consul Template으로 mTLS 인증서 자동 생성

이 페이지에서는 Vault의 PKI Secrets Engine으로 동적 X.509 인증서를 생성하고 갱신하며, Consul Template으로 인증서를 자동으로 교체하는 방법을 설명해요. 이 방법은 각 에이전트가 자동으로 교체되는 상대적으로 짧은 TTL의 고유한 인증서를 갖게 하여, 상호 TLS(mTLS)를 사용하면서 데이터센터를 안전하게 확장할 수 있게 해줘요.

출처: 문서

본문

이 페이지에서는 Vault의 PKI Secrets Engine을 사용해 동적 X.509 인증서를 생성하고 갱신하며, Consul Template을 사용해 인증서를 자동으로 교체하는 방법을 설명합니다.

이 방법은 Consul 데이터센터의 각 에이전트가 자동으로 교체되는 상대적으로 짧은 TTL(time-to-live)의 고유한 인증서를 갖게 하여, 상호 TLS(mTLS)를 사용하면서 데이터센터를 안전하게 확장할 수 있게 해줍니다.

Vault를 Consul 서비스 메시 인증 기관으로 사용하려면 Vault as Consul service mesh certification authority를 참조하세요.

사전 요구 사항 (Prerequisites)

  • Consul: 최소한 하나의 Consul 서버 노드와 이상적으로는 하나의 Consul 클라이언트 에이전트 노드가 필요합니다. Deploy Consul on VMs를 따라 Consul 에이전트를 배포하는 방법을 알아보세요. 이 페이지는 시나리오에 필요한 특정 구성을 제공합니다.
  • Vault: 네트워크에 실행 중인 Vault 클러스터가 필요합니다. 로컬 Vault dev 서버 또는 기존 Vault 배포를 사용할 수 있습니다. 또한 VAULT_ADDR, VAULT_CACERT, VAULT_TOKEN을 설정해 Vault 클러스터와 상호작용하도록 터미널을 구성해야 합니다.
  • Consul Template: 인증서 생성과 배포를 자동화하려면 에이전트의 노드에 consul-template 바이너리를 설치해야 합니다.

다음 다이어그램은 기능을 설명하는 데 필요한 최소 아키텍처를 보여줍니다.

참고

Nomad sandbox를 사용해 브라우저의 호스팅 터미널 세션에서 따라 할 수 있습니다. 이 샌드박스에는 사전 구성된 Consul 및 Vault 배포가 포함됩니다. 샌드박스가 로드된 후 Consul Template을 다운로드하려면 sudo apt-get update && sudo apt-get install consul-template을 실행하세요.

Vault를 Consul의 인증 기관으로 구성하려면 다음 단계를 따르세요.

  1. 루트 인증 기관(CA) 구성
  2. 중간 CA 구성
  3. Vault 역할 생성
  4. Consul용 인증서 생성
  5. (선택) consul-template로 인증서 교체 자동화

루트 인증 기관(CA) 구성

루트 CA 역할을 하는 단일 마운트 포인트를 갖는 것이 좋습니다. 루트 CA는 다른 PKI secrets engine의 중간 인증서 서명 요청(CSR)에 서명합니다. 이 설정을 통해 기존에 네트워크에 있는 외부 CA를 사용하고 Vault의 PKI 엔진을 중간 CA로만 사용할 수 있습니다.

이 지침에서 pki 경로는 루트 CA의 마운트 포인트입니다.

pki 경로에서 Vault의 PKI secrets engine을 활성화합니다.

$ vault secrets enable pki
Success! Enabled the pki secrets engine at: pki/

PKI secrets engine을 조정하여 최대 TTL(time-to-live)이 87600시간(10년)인 인증서를 발급합니다.

$ vault secrets tune -max-lease-ttl=87600h pki
Success! Tuned the secrets engine at: pki/

루트 인증서를 생성하고 CA_cert.crt에 저장합니다.

$ vault write -field=certificate pki/root/generate/internal \
    common_name="consul" \
    issuer_name="root-CA" \
    ttl=87600h > CA_cert.crt

이것은 새 자체 서명 CA 인증서와 개인 키를 생성합니다. Vault는 임대 기간(TTL)이 끝날 때 생성된 루트를 자동으로 폐기합니다. CA 인증서는 자체 인증서 폐기 목록(CRL)에 서명합니다.

조정 구성 (Tuning configuration)

내부 인증서 수명주기 정책에 맞게 TTL을 조정할 수 있습니다.

openssl x509 -text -noout -in CA_cert.crt로 생성된 인증서를 검사할 수 있습니다.

CA 및 CRL URL을 구성합니다.

$ vault write pki/config/urls \
    issuing_certificates="${VAULT_ADDR}/v1/pki/ca" \
    crl_distribution_points="${VAULT_ADDR}/v1/pki/crl"

예시 출력:

Key                              Value
---                              -----
crl_distribution_points          [https://127.0.0.1:8200/v1/pki/crl]
delta_crl_distribution_points    []
enable_templating                false
issuing_certificates             [https://127.0.0.1:8200/v1/pki/ca]
ocsp_servers

중간 CA 구성

중간 CA가 서명한 인증서가 루트 CA에서도 서명되도록 하려고 합니다.

인증서 서명 요청(CSR)을 생성하고 루트 CA로 서명하여 중간 CA 인증서를 생성합니다. 이렇게 하면 보안 침해가 발생할 경우 루트 CA에서 직접 중간 CA가 발급한 모든 인증서를 폐기할 수 있습니다.

중간 CA용 Vault secrets engine 활성화

pki_int 경로에서 PKI secrets engine을 활성화합니다.

$ vault secrets enable -path=pki_int pki
Success! Enabled the pki secrets engine at: pki_int/

pki_int secrets engine을 조정하여 최대 TTL(time-to-live)이 43800시간(5년)인 인증서를 발급합니다.

$ vault secrets tune -max-lease-ttl=43800h pki_int
Success! Tuned the secrets engine at: pki_int/

조정 구성 (Tuning configuration)

내부 인증서 수명주기 정책에 맞게 TTL을 조정할 수 있습니다.

중간 CA용 CSR 요청

중간 CA에 대한 인증서 서명 요청(CSR)을 요청하고 pki_intermediate.csr로 저장합니다.

$ vault write -format=json pki_int/intermediate/generate/internal \
    common_name="dc1.consul Intermediate Authority" \
    issuer_name="intermediate-CA" \
    | jq -r '.data.csr' > pki_intermediate.csr

이 명령은 출력이 없습니다.

CSR 서명 및 Vault로 인증서 가져오기

먼저 CSR에 서명하여 pem 파일을 생성합니다.

$ vault write -format=json pki/root/sign-intermediate \
    issuer_ref="root-CA" \
    csr=@pki_intermediate.csr \
    format=pem_bundle ttl="43800h" \
    | jq -r '.data.certificate' > intermediate.cert.pem

이 명령은 출력이 없습니다.

CSR이 서명되고 루트 CA가 인증서를 반환하면 이를 Vault로 다시 가져옵니다.

$ vault write pki_int/intermediate/set-signed [email protected]

예시 출력:

Key                 Value
---                 -----
existing_issuers    <nil>
existing_keys       <nil>
imported_issuers    [44d38d6c-14f4-38f4-1a40-95cd8cc04815 09a129d4-b853-ff48-efe9-3700f6b941cf]
imported_keys       <nil>
mapping             map[09a129d4-b853-ff48-efe9-3700f6b941cf: 44d38d6c-14f4-38f4-1a40-95cd8cc04815:48418626-88f5-22f4-348c-1841be4b3cc4]

Vault 역할 생성

역할은 자격 증명을 생성하는 데 사용되는 정책에 매핑되는 논리적 이름입니다.

$ vault write pki_int/roles/consul-dc1 \
  allowed_domains="dc1.consul" \
  allow_subdomains=true \
  generate_lease=true \
  max_ttl="720h"

예시 출력:

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                       [dc1.consul]
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                        true
issuer_ref                            default
key_bits                              2048
key_type                              rsa
key_usage                             [DigitalSignature KeyAgreement KeyEncipherment]
locality                              []
max_ttl                               720h
no_store                              false
not_after                             n/a
not_before_duration                   30s
organization                          []
ou                                    []
policy_identifiers                    []
postal_code                           []
province                              []
require_cn                            true
serial_number_source                  json-csr
server_flag                           true
signature_bits                        256
street_address                        []
ttl                                   0s
use_csr_common_name                   true
use_csr_sans                          true
use_pss                               false

역할에 대해 다음 옵션을 사용하고 있습니다.

  • allowed_domains: 역할의 도메인을 지정합니다. 이 명령은 Consul에 사용할 기본 구성인 dc1.consul을 도메인으로 사용합니다.
  • allow_subdomains: 클라이언트가 다른 역할 옵션이 허용하는 CN의 하위 도메인인 CN을 가진 인증서를 요청할 수 있는지 지정합니다. 참고: 여기에는 와일드카드 하위 도메인이 포함됩니다.
  • generate_lease: 이 역할에 대해 발급/서명된 인증서에 Vault 임대가 연결될지 지정합니다. 인증서가 임대와 연결되면 Vault revoke <lease_id>로 인증서를 CRL에 추가할 수 있습니다.

이것으로 Vault를 CA로 구성하는 것이 완료됩니다.

Consul용 인증서 생성

첫 번째 인증서를 생성하여 pki 엔진 구성을 테스트합니다.

$ vault write pki_int/issue/consul-dc1 \
    common_name="server.dc1.consul" \
    ttl="24h" | tee certs.txt

인증서 TTL 조정 (Certificate TTL tuning)

이 가이드에서는 인증서의 TTL을 24시간으로 설정하고 있습니다. 즉 이 인증서는 만료되기 전에 24시간 동안만 유효합니다. 테스트 환경에서 더 짧은 TTL을 사용해 TTL이 만료된 후 인증서가 제대로 폐기되는지 확인해 보세요.

예시 출력:

Key                 Value
---                 -----
lease_id            pki_int/issue/consul-dc1/Oip6uSVy9A4Y6RbPdEv7WuUf
lease_duration      23h59m59s
lease_renewable     false
ca_chain            [-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIUBHQpzIb+3nN64cjTXhyHwTkz+G0wDQYJKoZIhvcNAQEL
BQAwETEPMA0GA1UEAxMGY29uc3VsMB4XDTI1MTEyMDE0NTYyOVoXDTMwMTExOTE0
NTY1OVowLDEqMCgGA1UEAxMhZGMxLmNvbnN1bCBJbnRlcm1lZGlhdGUgQXV0aG9y
aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApu82Atvb913IGMZM
6vGu8ZkvqAVvGjqsj1/ciIyR55LSD6Wt9jknMWovX2YvIW5xN36ApEpWeQpccRzc
## ...
-----END CERTIFICATE----- -----BEGIN CERTIFICATE-----
## ...
-----END CERTIFICATE-----]
certificate         -----BEGIN CERTIFICATE-----
## ...
-----END CERTIFICATE-----
expiration          1763737500
issuing_ca          -----BEGIN CERTIFICATE-----
## ...
-----END CERTIFICATE-----
private_key         [REDACTED PRIVATE KEY]
private_key_type    rsa
serial_number       48:91:a9:7b:d0:a6:10:93:98:0f:ca:dc:ea:83:14:42:8e:c8:ef:e0

Consul 구성

인증서를 사용해 Consul 에이전트를 구성합니다.

다음 tls 스탠자를 사용해 Consul 서버 TLS를 구성합니다.

tls {
  defaults {
    verify_incoming = true
    verify_outgoing = true
    verify_server_hostname = true
    ca_file = "/etc/consul.d/ca.crt"
    cert_file = "/etc/consul.d/agent.crt"
    key_file = "/etc/consul.d/agent.key"
  }
}

auto_encrypt {
  allow_tls = true
}

Consul 서버에 TLS 암호화를 구성하려면 세 파일이 필요합니다.

  • ca_file - 다른 노드의 ID를 검증하는 CA(또는 중간) 인증서.
  • cert_file - Consul 에이전트 공개 인증서.
  • key_file - Consul 에이전트 개인 키.

첫 Consul 시작에서는 이전에 생성한 인증서를 사용합니다.

certs.txt에서 두 인증서와 개인 키를 추출하여 올바른 파일과 위치에 배치하려면 다음 명령을 사용하세요.

인증서 폴더를 만듭니다.

$ mkdir -p /etc/consul.d

루트 CA 인증서를 추출합니다.

$ grep -Pzo "(?s)(?<=issuing_ca)[^\-]*.*?END CERTIFICATE[^\n]*\n" certs.txt | sed 's/^\s*-/-/g' > /etc/consul.d/ca.crt

에이전트 인증서를 추출합니다.

$ grep -Pzo "(?s)(?<=certificate)[^\-]*.*?END CERTIFICATE[^\n]*\n" certs.txt | sed 's/^\s*-/-/g' > /etc/consul.d/agent.crt

에이전트 키를 추출합니다.

$ grep -Pzo "(?s)(?<=private_key)[^\-]*.*?END RSA PRIVATE KEY[^\n]*\n" certs.txt | sed 's/^\s*-/-/g' > /etc/consul.d/agent.key

consul-template로 인증서 교체 자동화

가이드 단계에서는 인증서 생성 시 매개변수로 ttl="24h"를 사용했으며, 이는 이 인증서가 만료되기 전에 24시간 동안만 유효함을 의미합니다.

인증서 수명에 대한 올바른 절충을 결정하는 것은 항상 보안과 민첩성 사이의 타협입니다. 보안을 낮추지 않는 가능한 세 번째 방법은 TTL이 만료될 때 Consul Template을 사용해 Consul의 인증서 갱신을 자동화하는 것입니다.

템플릿 파일 생성

다음 템플릿을 사용해 Consul Template이 Vault에서 해당 파일을 생성하고 검색하도록 지시합니다.

agent.crt.tpl (서버 전용)

{{ with secret "pki_int/issue/consul-dc1" "common_name=server.dc1.consul" "ttl=24h" "alt_names=localhost" "ip_sans=127.0.0.1"}}
{{ .Data.certificate }}
{{ end }}

템플릿은 새 인증서를 생성하기 위해 Vault가 노출하는 pki_int/issue/consul-dc1 엔드포인트를 사용합니다. 또한 인증서의 공통 이름과 대체 이름을 언급합니다.

agent.key.tpl (서버 전용)

{{ with secret "pki_int/issue/consul-dc1" "common_name=server.dc1.consul" "ttl=24h" "alt_names=localhost" "ip_sans=127.0.0.1"}}
{{ .Data.private_key }}
{{ end }}

같은 엔드포인트는 .Data.issuing_ca 매개변수 아래에 CA 인증서도 노출합니다.

ca.crt.tpl

{{ with secret "pki_int/issue/consul-dc1" "common_name=server.dc1.consul" "ttl=24h"}}
{{ .Data.issuing_ca }}
{{ end }}

새로 만든 파일을 /opt/consul/templates에 복사합니다.

$ cp *.tpl /opt/consul-template/

Consul Template 구성 생성

Consul Template이 Consul 에이전트(클라이언트와 서버)에 필요한 파일을 검색해 TLS 암호화를 구성하도록 지시하는 구성 파일 consul_template.hcl을 만듭니다.

consul_template.hcl

# This denotes the start of the configuration section for Vault. All values
# contained in this section pertain to Vault.
vault {
  # This is the address of the Vault leader. The protocol (http(s)) portion
  # of the address is required.
  address      = "https://localhost:8200"

  # This value can also be specified via the environment variable VAULT_TOKEN.
  token        = "root"

  unwrap_token = false

  renew_token  = false
}

# This block defines the configuration for a template. Unlike other blocks,
# this block may be specified multiple times to configure multiple templates.
template {
  # This is the source file on disk to use as the input template. This is often
  # called the "consul-template template".
  source      = "/opt/consul-template/agent.crt.tpl"

  # This is the destination path on disk where the source template will render.
  # If the parent directories do not exist, consul-template will attempt to
  # create them, unless create_dest_dirs is false.
  destination = "/etc/consul.d/agent.crt"

  # This is the permission to render the file. If this option is left
  # unspecified, consul-template will attempt to match the permissions of the
  # file that already exists at the destination path. If no file exists at that
  # path, the permissions are 0644.
  perms       = 0700

  # This is the optional command to run when the template is rendered. The
  # command will only run if the resulting template changes.
  command     = "sh -c 'date && consul reload'"
}

template {
  source      = "/opt/consul-template/agent.key.tpl"
  destination = "/etc/consul.d/agent.key"
  perms       = 0700
  command     = "sh -c 'date && consul reload'"
}

template {
  source      = "/opt/consul-template/ca.crt.tpl"
  destination = "/etc/consul.d/ca.crt"
  command     = "sh -c 'date && consul reload'"
}

서버 구성 파일에는 CA 인증서와 서버 에이전트의 인증서/키 쌍을 검색하는 정보가 포함되어 있습니다.

Consul Template이 Vault와 통신할 수 있게 하려면 다음 매개변수를 정의하세요.

  • address: Vault 서버의 주소. Vault가 Consul과 같은 노드에서 실행된다면 http://localhost:8200을 사용할 수 있습니다.
  • token: 적절한 권한이 있는 유효한 Vault ACL 토큰. 이 예시에서는 Vault 루트 토큰을 사용할 수 있습니다.

프로덕션 확인 (Production check)

Vault 루트 토큰 사용은 프로덕션에 권장되지 않습니다. 권장 보안 접근 방식은 제한된 권한을 가진 특정 정책을 기반으로 새 토큰을 만드는 것입니다.

Consul Template 시작

구성 파일을 제공하기 위해 -config 매개변수를 사용해 Consul Template을 시작합니다.

$ consul-template -config "consul_template.hcl"

Configuration reload triggered

인증서 교체 확인

Consul 서버에 대해 수동으로 만든 인증서의 TTL은 24시간이었습니다.

이는 인증서가 만료된 후 Vault가 이를 갱신하고 Consul Template이 에이전트의 파일을 업데이트한 다음 새 파일을 적용하도록 Consul 구성을 자동으로 다시 로드한다는 의미입니다.

Consul Template이 24시간마다 타임스탬프와 다음 로그 줄을 계속 나열하는지 확인하여 교체를 검증하세요.

Configuration reload triggered

openssl로 인증서 내용을 확인합니다.

$ openssl x509 -text -noout -in /etc/consul.d/agent.crt

출력에서 Not Before와 Not After 값이 새 인증서를 반영하도록 업데이트되고 있는지 확인하세요.

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            0a:06:b2:62:56:d1:7d:e6:5c:67:f7:89:06:8a:3e:5b:f1:fa:7e:00
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=dc1.consul Intermediate Authority
        Validity
            Not Before: Nov 25 11:48:06 2025 GMT
            Not After : Nov 25 12:48:06 2025 GMT
        Subject: CN=server.dc1.consul
## ...

다음 단계 (Next steps)

이 페이지에서는 Vault의 PKI secret engine을 사용해 Consul TLS 인증서를 생성하고 Consul Template으로 자동 교체하는 방법을 배웠습니다.

Vault와 consul-template을 사용해 Consul의 gossip 암호화를 자동화하려면 Generate and manage gossip encryption for Consul with Vault and Consul Template을 참조하세요.

Vault를 사용해 Consul의 ACL 토큰을 생성하고 관리하는 방법을 배우려면 Use Vault for ACL management with Consul on VMs을 참조하세요.

Kubernetes에서 Consul을 실행한다면 use Vault for secrets management with Consul on Kubernetes을 사용할 수 있습니다.

더 알아보기 (Learn more)