NGINX Key-Value 사용하여 HashiCorp Vault에서 동적 SSL 인증서 관리
NGINX Plus Key-Value Store와 HashiCorp Vault를 구성하고 통합하여 사용해 실시간 Vault 인증서 발급 요청을 NGINX Plus의 동적 인증서 로드 기능을 통해 요청하고 통합하는 방법을 설명합니다.
많은 상황에서 SSL 인증서 데이터를 디스크에 저장하는 것은 해당 인증서에 대한 액세스를 제어하기 위해 추가 보안 가드레일을 사용하는 한 허용 가능한 위험입니다. 그러나 일부 사용 사례에서는 모든 보안 관련 구성 요소를 디스크에 저장하지 않고 메모리에 저장하고 메모리에서만 액세스해야 하는 추가 요구 사항이 있습니다.
이 포스트에서는 임시 SSL 인증서 키에 중점을 둡니다. HashiCorp Vault를 사용하여 임시 SSL 인증서를 발급하고 메모리 내 데이터베이스인 NGINX Plus Key-Value Store에 저장합니다.
NGINX Plus R18 이상은 SSL 인증서 키 쌍을 메모리에 로드하고 NGINX Plus Key-Value Store의 값과 같은 변수를 통해 액세스할 수 있기 때문에 안전한 SSL 키 관리를 위한 강력한 아키텍처를 지원합니다. NGINX Plus, Key-Value Store를 통한 SSL 키 저장 및 관리, HashiCorp Vault를 함께 사용하면 중요한 임시 SSL 데이터를 디스크에 Commit 하지 않고 저장하여 SSL 인증서에 대한 보안 환경을 만들 수 있습니다.
HashiCorp Vault는 SSL 인증서 발급 외에도 보안 데이터 관리하기 위한 다양한 기능을 제공합니다. 전송 중인 데이터를 보호하기 위해 임시 인증서가 주로 필요하고 해당 인증서 또는 인증서를 사용하는 시스템이 자주 순환하는 환경에서는 Vault를 인증서 발급 기관(CA)으로 활용할 수 있습니다.
목차
1. 전제조건
2. Architectural Design
3. 임시 인증서 CA로 HashiCorp Vault 구성
3-1. Vault를 CA로 구성
3-1-1. NGINX Plus 인증서 요청에 PKI 활성화
3-1-2. CA 발급자 생성
3-1-3. CA에서 Endpoint 및 Role 구성
3-2. NGINX Plus 인스턴스에 대한 읽기 전용 정책 생성
4. SSL 저장소용 NGINX Plus Key-Value Store 구성
5. NGINX Plus Key-Value Store에 SSL PEM 데이터 로드
6. Vault CA 인증서 요청을 NGINX Plus Key-Value Store와 통합
6-1. NGINX Plus Key-Value Store 에 새 임시 인증서/키 PEM 요청 및 로드
6-2. 인증서 업데이트 및 취소
7. 결론
1. 전제조건
다음과 같은 가정을 합니다.
- Vault에 어느 정도 익숙합니다.
- Vault가 이미 설치되어 구성되어 있고 서비스로 실행 중에 있습니다(Vault 설명서 참조)
- Vault 인스턴스에 대한 관리자 수준 액세스 권한이 있습니다.
- 표시된 인스턴스에서 다음 환경 변수가 구성됩니다.
- Vault 인스턴스:
- VAULT_ADDR=https://127.0.0.1:8200
- VAULT_EXT=https://externally_accessible_Vault_IP_address:8200
- VAULT_TOKEN=initial_root_token
- SSL management 인스턴스:
- VAULT_EXT=https://externally_accessible_Vault_IP_address:8200
- VAULT_APP_TOKEN=NGINX_role_token
2. Architectural Design
가장 기본적인 샘플 배포에서 일부 유형의 SSL 요청/사후 관리 도구를 사용하여 Vault에서 임시 인증서를 요청하고 이를 NGINX Plus Key-Value Store에 로드하는 데 사용됩니다. 이 예에서는 간단한 curl 명령을 사용하여 SSL 요청/게시 도구를 시뮬레이트합니다. 테스트를 위해 동일한 시스템, 다른 시스템, 컨테이너 등에 Vault와 NGINX Plus를 모두 설치할 수 있습니다. 인증서를 요청하고 NGINX Plus에 로드하는 데 사용하는 도구(Curl)가 HTTPS를 통해 Vault 및 NGINX Plus와 통신할 수 있어야 합니다.
다음 다이어그램은 다음과 같은 아키텍처를 보여줍니다.
- 요청/게시 도구는 API 호출을 통해 Vault에서 새로운 임시 PEM을 요청합니다.
- 이 도구는 NGINX Plus API를 통해 메모리 내 Key-Value Store에 임시 PEM 데이터를 기록하고 프로세스에서 디스크에 기록하지 않습니다.
- HTTPS 클라이언트는 임시 인증서를 사용하는 NGINX Plus에서 https://www.example.com을 요청합니다.

3. 임시 인증서 CA로 HashiCorp Vault 구성
임시 인증서용 CA로 Vault를 구성하는 두 가지 주요 절차가 있습니다.
- Vault를 CA로 구성
- NGINX Plus 인스턴스에 대한 읽기 전용 정책 생성
3-1. Vault를 CA로 구성
테스트 목적으로 Vault의 CA 기능을 사용하여 임시 인증서를 생성하고 서명합니다. 이를 통해 Vault를 이러한 임시 인증서에 대한 One‑Stop Endpoint로 사용할 수 있지만 아키텍처에 다른 도구가 필요한 경우 필요에 따라 조정할 수 있습니다.
Vault를 발급 CA로 사용하려면 먼저 새 임시 인증서 및 키를 생성하고 발급하도록 공개 키 인프라(PKI) 저장소를 구성합니다. 다음 명령은 localhost 인스턴스에서 Vault를 구성할 때 적합합니다.
3-1-1. NGINX Plus 인증서 요청에 PKI 활성화
먼저 NGINX Plus 요청에 대한 사용자 정의 Endpoint을 정의하고 구성을 확인하여 PKI 지원을 활성화합니다.
# vault secrets enable -path pki/nginx-plus-ephem-certs pki
# vault secrets list
3-1-2. CA 발급자 생성
임시 인증서 요청에만 사용할 새 CA를 Vault 내에 생성합니다. 새 인증서를 만들지 않고 기존 CA 인증서를 Vault로 가져올 수도 있습니다. CA 세부 정보 가져오기에 대한 지침과 PKI Endpoint 구축을 위한 추가 옵션에 대한 정보는 Vault 설명서를 참조하십시오.
1주일(168시간) 동안 유효한 CA를 생성하고 안전하게 보관하기 위해 JSON 형식의 CA 인증서 키 쌍을 파일에 기록합니다.
# vault write -format=json pki/nginx-plus-ephem-certs/root/generate/internal common_name="Example\ Company" ttl=168h > NGINX-Plus-Ephem-CA.json
출력을 로컬 JSON 파일에 저장하는 것은 필수가 아닙니다. 액세스 가능한 디스크에 CA 세부 정보를 보관하는 것보다 CA를 재생성하는 것이 합리적인 개발 환경 또는 매우 안전한 환경에서는 JSON을 저장하거나 CA 세부 정보를 Vault의 보안 Key Store에 다시 저장할 수 없습니다.
3-1-3. CA에서 Endpoint 및 Role 구성
로컬 발급 CA 역할을 하도록 구성된 Vault를 사용하여 임시 인증서 발급을 시작할 수 있습니다. 먼저 NGINX Plus 인스턴스에 대한 인증서 해지 목록(CRL) Endpoint를 통해 요청을 지원하도록 Vault를 구성합니다.
# vault write pki/nginx-plus-ephem-certs/config/urls issuing_certificates="$VAULT_EXT/v1/pki/nginx-plus-ephem-certs/ca" crl_distribution_points="$VAULT_EXT/v1/pki/nginx-plus-ephem-certs/crl"
이제 NGINX Plus 인스턴스에 대해 Vault에 할당되는 역할을 생성하여 이러한 인스턴스가 CA에서 새 인증서를 요청할 수 있도록 합니다. 새 역할을 구성할 때 역할별 인증서 요구 사항을 정의할 수 있습니다. 예를 들어 NGINX 인증서 요청 역할에 대한 두 가지 옵션이 있습니다. 하나는 인증서 요청에 명시적 도메인을 포함하도록 강제하는 옵션이고 다른 하나는 로컬 CA에 대해 서명된 CN(Common Name)에 대한 인증서를 생성할 수 있도록 하는 옵션입니다. 이 사용 사례에서는 두 샘플 역할 모두 인증서 내에서 SAN(주체 대체 이름) 문자열을 지원합니다.
Note: 다음 명령은 예제일 뿐이며 각 명령에는 프로덕션 환경에서 사용하기 전에 검토해야 하는 보안 관련 사항이 있습니다.
인증서 요청을 특정 하위 도메인으로 제한하고 SAN 항목을 허용합니다.
# vault write pki/nginx-plus-ephem-certs/roles/nginx-cert-requests allowed_domains=example.com allow_subdomains=true allow_ip_sans=true allow_alt_names=true key_bits=2048 max_ttl=72h no_store=true
또는 모든 CN 및 SAN 항목을 허용합니다.
# vault write pki/nginx-plus-ephem-certs/roles/nginx-cert-requests allow_any_name=true allow_ip_sans=true allow_alt_names=true key_bits=2048 max_ttl=72h no_store=true
3-2. NGINX Plus 인스턴스에 대한 읽기 전용 정책 생성
마지막으로 특정 유형의 인증서 요청만 허용하는 HashiCorp 구성 언어(HCL) 정책을 Vault에 추가합니다. Vault 루트 액세스 토큰을 공유하거나 일상적인 작업에 사용하지 않는 것이 좋습니다. 따라서 이 정책에 연결된 NGINX Plus 인스턴스용으로 특별히 액세스 토큰을 생성합니다.
먼저 다음 콘텐츠로 nginx-cert-requests.hcl 파일을 만들고 /etc/vault 디렉터리에 저장하여 새 PKI Endpoint에 대한 HCL 정책을 정의합니다.
path "pki/nginx-plus-ephem-certs/issue/*" {
capabilities = ["create","update"]
}
PKI Endpoint에 대한 액세스를 추가로 제한하는 데 사용할 수 있는 더 많은 옵션이 있지만 이 예에서는 단순히 새 인증서를 요청할 수 있는 권한을 부여합니다.
다음으로 정책을 Vault에 로드합니다.
# vault policy write nginx-cert-requests /etc/vault/nginx-cert-requests.hcl
이제 NGINX Plus 인스턴스에 대한 임시 인증서를 요청하는 데 사용되는 이 새로운 읽기 전용 정책에 대한 고유한 액세스 토큰을 생성합니다.
# vault token create -policy=nginx-cert-requests
이 명령에 의해 반환되는 토큰 값은 인증서를 요청할 시스템의 VAULT_APP_TOKEN이라는 환경 변수에 저장합니다. 이것은 SSL 관리 도구를 통해 Vault에서 새 PEM 데이터를 요청하고 SSL 데이터를 Key-Value Store로 로드하는 데 사용되는 토큰입니다.
보안 수준이 높은 환경에서는 읽기 전용 토큰에 짧은 TTL(Time-To-Live)을 설정하여 반복적으로 다시 생성하고 재배포할 수 있습니다. 토큰 생성에 대한 추가 세부 정보 및 구성 옵션은 Vault 설명서를 참조하십시오.
이제 안전한 Vault CA에서 새 임시 인증서를 요청할 준비가 되었습니다. 새 인증서를 요청할 수 있는지 확인하기 위해 요청 명령을 실행하고 결과 인증서를 Key-Value Store로 로드할 시스템에서 다음 명령을 실행합니다. 이 명령은 필요한 인증서 요청 정보(예: CN, SAN 및 기간)를 Vault에 전달하고 Vault는 인증서 및 키 페이로드를 JSON 형식으로 생성합니다. 아래 API 호출에서 Vault에 제출하는 JSON은 보다 읽기 쉬운 형태로 제공됩니다.
# curl -ks --header "X-Vault-Token: $VAULT_APP_TOKEN" -X POST -d '{"common_name":"www.example.com","ip_sans":"10.10.1.1,192.168.76.76","alt_names":"dev.example.com,eng.example.com","ttl":"1h","format":"pem_bundle"}' $VAULT_EXT/v1/pki/nginx-plus-ephem-certs/issue/nginx-cert-requests
결과 JSON에는 발급 CA에 대한 SSL 인증서, 키 및 데이터가 포함됩니다. Vault를 구성한 방식으로 인해 이 인증서 데이터는 이 JSON 응답에만 존재하며 Vault는 이를 저장하지 않습니다. 테스트를 위해 이것이 원하는 응답이지만 수동 테스트를 위해 SSL 데이터를 저장해야 하는 경우 출력을 jq와 같은 도구로 Pipe 할 수 있습니다.
4. SSL 저장소용 NGINX Plus Key-Value Store 구성
NGINX Plus는 메모리 내 Key-Value Store를 통해 SSL 인증서와 키를 저장하고 읽을 수 있으므로 디스크가 아닌 메모리에서 직접 인증서를 로드할 수 있습니다. 또한 NGINX Plus는 SNI(Server Name Indication) 호스트 또는 기타 기준과 같은 실시간 세션 데이터를 기반으로 다양한 인증서를 사용할 수 있습니다.
SSL 데이터를 메모리에서 직접 로드하도록 NGINX Plus를 구성하려면 ssl_certificate 및 ssl_certificate_key 지시문에 매개변수 접두사를 data:로 추가합니다. 접두사는 NGINX Plus가 문자열을 메모리 내 Key-Value Store에서 직접 변수로 제공할 수 있는 Raw 인증서 키 PEM 콘텐츠로 해석하도록 지시합니다.
따라서 디스크 저장소에 있는 플랫 파일 인증서 및 키를 저장하고 배포할 필요가 없으므로 더 이상 배포 이미지에 저장 및 액세스할 수 없으며 백업에 저장하는 등의 작업을 수행할 수 없습니다.
다음 예에서는 다음 기능을 사용하여 NGINX Plus 구성을 구축하고 있습니다.
- 결합된 인증서 키 PEM 문자열을 저장하는 vault_ssl_pem이라는 Key-Value Store입니다.
- 인증서는 들어오는 요청의 SNI 호스트 헤더 값을 기준으로 연결 시간에 일치합니다(예: 인증서는 호스트 이름이 http://www.example.com과 같이 구성된 키와 동일한 경우에만 Key-Value Store에서 로드됨).
- SSL PEM 데이터는 포트 443에 바인딩된 server 블록에 로드됩니다.
- 테스트를 위해 디버그 로깅이 활성화되어 있으므로 액세스 로그의 각 연결에 사용되는 전체 PEM 문자열을 캡처할 수 있습니다. (많은 양의 데이터를 생성하고 보안 위험이 발생할 수 있으므로 프로덕션 환경에서는 이 구성을 권장하지 않습니다.)
- NGINX Plus API Endpoint는 /api 위치에 대한 HTTPS 트래픽만 허용하도록 구성되었습니다.
다음 내용으로 /etc/nginx/conf.d에 ssl_keyval.conf라는 구성 파일을 만듭니다.
log_format vault_ssl_keyval '$remote_addr [$time_local] - '
'ssl_server_name:"$ssl_server_name" '
'host:"$host" ';
keyval_zone zone=vault_ssl_pem:1m;
keyval $ssl_server_name $certificate_pem zone=vault_ssl_pem;
server {
listen 443 ssl;
access_log /var/log/nginx/vault-ssl-keystore-access.log vault_ssl_keyval;
error_log /var/log/nginx/vault-ssl-keystore-error.log debug;
# Load PEMs from variable. Note the 'data:' prefix.
ssl_certificate data:$certificate_pem;
ssl_certificate_key data:$certificate_pem;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
server {
listen 8443 ssl;
access_log /var/log/nginx/status-api-access.log api;
error_log /var/log/nginx/status-api-error.log notice;
ssl_certificate /etc/nginx/ssl/nginx-ssl.crt;
ssl_certificate_key /etc/nginx/ssl/nginx-ssl.key;
location /api {
api write=on;
if ($request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|PATCH|DELETE)$) {
return 405;
}
}
}
5. NGINX Plus Key-Value Store에 SSL PEM 데이터 로드
이제 NGINX Plus API를 통해 PEM 데이터를 vault_ssl_pem Key-Value Store에 직접 로드할 수 있습니다.
NGINX Plus API를 읽기-쓰기로 활성화했습니다. SSL 키와 같은 민감한 데이터를 로드할 때 NGINX Plus API에 대해 SSL 및 인증을 활성화하는 것이 좋습니다. 따라서 아래 예제는 포트 8443에서 HTTPS를 통해 NGINX Plus API에 액세스합니다.
다음 명령을 실행하여 http://www.example.com에 대한 PEM 데이터(또는 이 경우 예를 들어 SSL PEM 키의 첫 번째 줄)를 Key-Value Store로 로드하여 NGINX Plus가 올바르게 구성되었는지 테스트할 수 있습니다. NGINX_Plus_instance는 NGINX Plus API를 구성한 NGINX Plus 인스턴스의 호스트 이름입니다.
# curl -s -X POST -d '{"www.example.com":"-----BEGIN RSA PRIVATE KEY-----\n..."}' https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
그런 다음 NGINX Plus API를 통해 vault_ssl_pem Key-Value Store를 쿼리하여 PEM 인증서와 키 데이터가 http://www.example.com 키와 연결된 값인지 확인합니다.
# curl https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
{"www.example.com":"-----BEGIN RSA PRIVATE KEY-----\n..."}
이 시점에서 우리는 포트 443에서 http://www.example.com이라는 서비스에 대한 모든 HTTPS 연결에 대해 메모리 내 Key-Value Store에서 PEM 인증서 및 키 데이터를 동적으로 검색하도록 NGINX Plus를 구성했습니다.
http://www.example.com 키와 연결된 SSL PEM 데이터를 제거하기 위해 null 값을 사용하여 NGINX Plus API에 대한 PATCH 호출을 수행할 수 있습니다.
# curl -s -X PATCH -d '{"www.example.com":null}' https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
6. Vault CA 인증서 요청을 NGINX Plus Key-Value Store와 통합
6-1. NGINX Plus Key-Value Store 에 새 임시 인증서/키 PEM 요청 및 로드
마지막 단계는 Vault에서 임시 SSL 키 인증서 PEM 번들을 요청하고 이를 NGINX Plus Key-Value Store에 로드하는 것입니다. 임시 인증서 또는 강화된 보안 환경을 처리할 때 새로운 PEM 데이터를 요청하고 데이터가 디스크에 저장되지 않도록 한 번의 유동적인 단계로 내부 Key-Value Store에 로드하는 것이 이상적입니다. 이를 위해 위의 예에서 Vault에 대한 PEM 요청과 NGINX Plus API에 대한 Key-Value POST 요청을 단일 명령으로 결합하기만 하면 됩니다. SSL 관리 도구 또는 Vault API와 NGINX Plus API에 모두 액세스할 수 있는 다른 중앙 위치에서 실행할 수 있습니다.
# echo "{\"www.example.com\":$( curl -ks --header "X-Vault-Token: $VAULT_APP_TOKEN" -X POST -d '{"common_name":"www.example.com","ip_sans":"10.10.1.1,192.168.76.76","alt_names":"dev.example.com,eng.example.com","ttl":"1h","format":"pem_bundle"}' $VAULT_EXT/v1/pki/nginx-plus-ephem-certs/issue/nginx-cert-requests | jq '.data | "\(.certificate)"' )}" | curl -ks -X POST -d @- https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
- echo 명령은 문자열을 NGINX Plus API에 전달합니다.
- 첫 번째 curl 명령은 새로 생성된 Vault CA에서 JSON 형식의 키 인증서 PEM을 요청합니다.
- jq 명령은 Vault CA에서 생성된 응답에서 PEM 데이터만 추출합니다.
- 마지막 curl 명령은 NGINX Plus API에 대한 POST 호출을 수행하여 PEM 데이터를 vault_ssl_pem Key-Value Store에 삽입합니다.
이전 섹션에서와 같이 vault_ssl_pem Key-Value Store를 쿼리 하여 인증서 키 PEM 데이터가 Key-Value Store의 http://www.example.com 키와 연결된 값인지 확인합니다.
# curl https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
{"www.example.com":"-----BEGIN RSA PRIVATE KEY-----…"}
새 PEM 데이터 문자열이 http://www.example.com 도메인의 Key-Value Store에 로드되면 적용 가능한 NGINX Plus 서비스에 대한 모든 새 HTTPS 연결은 이 인증서와 키를 사용하여 연결을 보호합니다. 이 명령을 실행하여 이것이 작동하는지 테스트합니다.
# curl https://www.example.com
Key-Value Store에 http://www.example.com 키에 대한 유효한 PEM 데이터가 있다고 가정하면 HTTPS 요청이 성공합니다. NGINX Plus 인스턴스에 대한 curl 명령에서 해당 이름에 해당하는 IP 주소와 같이 http://www.example.com 이외의 항목을 사용하여 Key-Value Store의 PEM 데이터에 대한 SNI 일치가 올바르게 작동하는지 테스트할 수 있습니다. 이로 인해 SSL 핸드셰이크가(Handshake) 실패하고 오류 로그(/var/log/nginx/vault-ssl-keystore-error.log)에서 확인할 수 있습니다.
# curl https://IP-address-for-www.example.com
6-2. 인증서 업데이트 및 취소
호스트와 연결된 PEM 데이터를 변경, 취소 또는 제거해야 하는 경우 API PATCH 메서드를 활용하여 Key-Value Store에서 http://www.example.com의 값을 업데이트하는 위와 유사한 접근 방식을 따를 수 있습니다. 이는 매일 또는 인증서를 취소해야 하는 경우와 같이 고정된 시간 단위로 정기적으로 SSL 인증서를 순환하는 훌륭한 사용 사례가 될 수 있습니다.
Vault에서 새 인증서를 요청하여 Key-Value Store에서 http://www.example.com과 연결된 값을 업데이트하려면 이전 섹션과 유사한 명령을 실행하되 두 번째 curl 명령에서 HTTP 메서드를 POST에서 PATCH로 변경하여 API를 통해 업데이트된 PEM 번들을 Key-Value Store로 Push 합니다.
# echo "{\"www.example.com\":$( curl -ks --header "X-Vault-Token: $VAULT_APP_TOKEN" -X POST -d '{"common_name":"www.example.com","ip_sans":"10.10.1.1,192.168.76.76","alt_names":"dev.example.com,eng.example.com","ttl":"1h","format":"pem_bundle"}' $VAULT_EXT/v1/pki/nginx-plus-ephem-certs/issue/nginx-cert-requests | jq '.data | "\(.certificate)"' )}" | curl -ks -X PATCH -d @- https://NGINX_Plus_instance:8443/api/6/http/keyvals/vault_ssl_pem
http://www.example.com 키와 관련된 값을 완전히 제거하거나 취소하려면 null 값과 함께 PATCH를 사용하십시오.
# curl -s -X PATCH -d '{"www.example.com":null}' https://NGINX_Plus_instance:8443/api/6/http/key-vals/vault_ssl_pem
7. 결론
모든 것이 예상대로 작동하는지 확인한 후 이러한 명령을 Vault 및 NGINX Plus API에 스크립팅 하여 필요에 따라 인증서를 교체할 수 있습니다. SSL 인증서 및 키 관리, NGINX Plus 배포 또는 NGINX Plus Key-Value Store관리에 이미 사용 중인 모든 CI/CD Pipeline에 이 Workflow를 추가할 수도 있습니다. 고유한 CN 및 SAN 지시문과 같은 추가 인증서 요청 세부 정보를 Vault에 제공하여 특정 서비스 이름 및 기타 지정된 기준에서만 작동하는 인증서를 생성할 수 있습니다. 이 아키텍처를 사용하면 사용자 지정된 임시 인증서를 생성하고 메모리에 저장하는 매우 구체적인 시스템을 사용할 수 있으므로 디스크에 아무것도 기록되지 않습니다.
NGINX Plus를 직접 사용해 보거나 테스트해 보려면 지금 30일 무료 평가판을 신청하거나 사용 사례에 대해 최신 소식을 빠르게 전달받고 싶으시면 아래 뉴스레터를 구독하세요.
댓글을 달려면 로그인해야 합니다.