Vault Agent와 PKI 외부 CA로 인증서 자동화하기

Vault Agent와 PKI 외부 CA로 인증서 자동화하기

Enterprise

적절한 Vault Enterprise 라이선스가 필요합니다.

Vault Agent는 공개 인증 기관(CA)을 위한 ACME 클라이언트 역할을 하여, 수동 운영자 개입 없이 전체 인증서 수명주기를 자동화할 수 있습니다. Agent가 인증서를 발급하거나 갱신하면, 이를 참조하는 모든 템플릿을 자동으로 다시 렌더링합니다.

출처: 문서

본문

요구 사항 (Requirements)

  • Vault Enterprise v2.0.0 이상
  • ACME 워크플로우용으로 구성된 Vault PKI 외부 CA 시크릿 엔진 마운트. 설정 지침은 PKI 외부 CA 시크릿 엔진을 참조하세요.
  • 에이전트의 인증 방법이 PKI 마운트의 acme/* 경로와 대상 roles/<role>/acme/ 경로에 대한 update 접근을 허용해야 합니다.
  • HTTP-01 챌린지용: 기존 웹 서버가 서빙하는 쓰기 가능한 파일시스템 경로, 또는 Agent가 임시 리스너를 바인딩할 수 있는 사용 가능한 TCP 포트.

구성 (Configuration)

에이전트 구성 파일에 하나 이상의 pki_external_ca 스탠자를 추가합니다. 각 블록은 템플릿에서 참조하는 고유한 라벨을 가져야 합니다.

최소 예시 (다중 챌린지 경로를 사용한 HTTP-01)

다음 예시는 기존 웹 서버를 사용해 ACME HTTP-01 챌린지 파일을 서빙합니다.

vault {
  address = "https://vault.example.com:8200"
}

auto_auth {
  method "kubernetes" {
    config {
      role = "my-app"
    }
  }
}

pki_external_ca "web-tls" {
  mount_path     = "pki"
  role           = "web-server"
  challenge_type = "http-01"

  identifiers {
    dns = ["app.example.com"]
  }

  http_01 {
    challenge_path = "/var/www/html/.well-known/acme-challenge"
  }

  destination {
    path       = "/etc/certs"
    pem_bundle = false
  }
}

template {
  destination = "/etc/nginx/tls/app.crt"
  contents    = <<-EOT
    {{ with pkiCertExternalCa "web-tls" -}}
    {{ .Certificate }}
    {{- end }}
  EOT
}

전체 예시 (RSA 키가 있는 명시적 CSR)

다음 예시는 2048비트 RSA 키가 있는 명시적 CSR을 사용합니다. Agent는 ACME 챌린지를 서빙하기 위해 포트 8402에 임시 HTTP 리스너를 바인딩합니다.

pki_external_ca "internal-api" {
  mount_path                  = "pki-internal"
  role                        = "api-server"
  namespace                   = "team-a"
  challenge_type              = "http-01"
  percent_renew_before_expiry = 30

  csr {
    CN  = "api.internal.example.com"
    C   = "US"
    ST  = "California"
    L   = "San Francisco"
    O   = "Example Corp"
    OU  = "Engineering"
    SANs = ["api.internal.example.com", "api-v2.internal.example.com"]

    private_key {
      rsa {
        bits = 2048
      }
    }
  }

  http_01 {
    listener_addr = "0.0.0.0:8402"
  }

  destination {
    path            = "/etc/pki/api"
    pem_bundle      = true
    filename_prefix = "bundle"
    umask           = "077"
  }
}

pki_external_ca 스탠자 참조

Parameter Required Default Description
<label> no — 이 블록의 고유한 이름으로, 템플릿에서 pkiCertExternalCa의 인자로 사용됩니다.
mount_path yes — PKI 시크릿 엔진 마운트 경로(예: pki).
role yes — 인증서를 발급하는 데 사용되는 PKI 역할.
namespace no vault.namespace에서 상속 이 블록에 대한 Vault 네임스페이스 오버라이드.
challenge_type no "http-01" ACME 챌린지 유형. 현재는 http-01만 지원됩니다.
percent_renew_before_expiry no 20 갱신이 트리거되는 남은 TTL 비율. 1과 99 사이여야 합니다.
identifiers no¹ — 자동 생성 CSR 식별자 블록. csr과 상호 배타적.
csr no¹ — 명시적 CSR 구성 블록. identifiers와 상호 배타적.
http_01 challenge_type = "http-01"일 때 yes — HTTP-01 챌린지 구성 블록.
destination yes — 발급된 인증서와 키의 출력 경로 구성.

¹ identifiers 또는 csr 중 정확히 하나가 필요합니다.

identifiers 블록

Parameter Required Description
dns yes 자동 생성 CSR에 포함할 DNS 이름 목록. 항목이 하나 이상 필요합니다.

csr 블록

Parameter Required Description
CN yes 인증서 주체의 일반 이름(Common Name).
private_key yes 키 구성 하위 블록.
C no 인증서 주체의 국가 필드.
ST no 인증서 주체의 주(state) 필드.
L no 인증서 주체의 지역(locality) 필드.
O no 인증서 주체의 조직(organization) 필드.
OU no 인증서 주체의 조직 단위(organizational unit) 필드.
SANs no 주체 대체 이름(Subject Alternative Names). 각 항목은 비어 있지 않아야 합니다.

csr.private_key 블록

rsa 또는 ecdsa 중 정확히 하나가 필요합니다.

rsa 하위 블록

Parameter Required Description
bits yes RSA 키 크기(비트). 최소값: 2048.

ecdsa 하위 블록

Parameter Required Description
type yes ECDSA 곡선. 지원 값: p256, p384, p521.

http_01 블록

challenge_path 또는 listener_addr 중 정확히 하나가 필요합니다.

Parameter Required Description
challenge_path no 챌린지 파일을 서빙하는 기존 웹 서버의 파일시스템 경로(예: /var/www/html/.well-known/acme-challenge).
listener_addr no 챌린지 응답용 임시 HTTP 리스너를 바인딩할 host:port. 호스트와 포트가 모두 있어야 하며, 포트는 1과 65535 사이여야 합니다.

destination 블록

Parameter Required Default Description
path yes — Agent가 인증서 파일을 쓰는 기존 디렉토리. pki_external_ca 블록 간 중복 경로는 허용되지 않습니다.
pem_bundle yes — true면 단일 <filename_prefix>.pem 번들을 씁니다. false면 별도의 <filename_prefix>.crt와 <filename_prefix>.key 파일을 씁니다.
filename_prefix no "cert" 출력 파일의 기본 파일 이름. 경로 구분자(/, \)나 파일 확장자(.pem, .crt, .key)를 포함해서는 안 됩니다.
umask no "077" 인증서 파일을 쓸 때 적용되는 8진수 umask.

템플릿 함수: pkiCertExternalCa

pkiCertExternalCa 템플릿 함수를 사용해 명명된 pki_external_ca 블록의 인증서 데이터에 접근합니다. 이 함수는 정확히 하나의 인자, 즉 블록 라벨을 받습니다.

{{ pkiCertExternalCa "" }}

반환 값은 .Certificate, .PrivateKey, .IssuingCA 필드를 포함해 Vault PKI issue 응답과 일치하는 객체입니다. 인증서가 아직 발급되지 않았다면 함수는 nil을 반환합니다. 에이전트 시작 중 이를 방어하려면 {{ with }} 블록을 사용하세요.

template {
  destination = "/etc/app/tls.crt"
  contents    = <<-EOT
    {{ with pkiCertExternalCa "web-tls" -}}
    {{ .Certificate }}
    {{ .IssuingCA }}
    {{- end }}
  EOT
}

template {
  destination = "/etc/app/tls.key"
  contents    = <<-EOT
    {{ with pkiCertExternalCa "web-tls" -}}
    {{ .PrivateKey }}
    {{- end }}
  EOT
}

템플릿 자동 다시 렌더링

어떤 pki_external_ca 블록이 인증서를 발급하거나 갱신하면 Agent가 템플릿 렌더링 러너를 다시 시작합니다. pkiCertExternalCa를 호출하는 모든 템플릿이 자동으로 다시 렌더링됩니다. 폴링이나 수동 재시작이 필요 없습니다.

고려 사항 (Considerations)

  • 블록 라벨은 단일 에이전트 구성 파일의 모든 pki_external_ca 스탠자에서 고유해야 합니다.
  • 대상 path 값은 고유해야 하며 기존 디렉토리를 가리켜야 합니다. Agent는 이들을 만들지 않습니다.
  • 기본 umask인 077은 에이전트 프로세스 소유자만 기록된 키 파일을 읽을 수 있음을 의미합니다.
  • percent_renew_before_expiry는 인증서의 남은 TTL을 기준으로 평가됩니다. 20 값은 인증서 수명의 20%가 남았을 때 갱신을 트리거합니다.

더 알아보기 (Learn more)