GitLab CI/CD에서 HashiCorp Vault 시크릿 사용하기
GitLab CI/CD에서 HashiCorp Vault 시크릿 사용하기
CI/CD 잡에서 비밀번호나 토큰 같은 민감한 값을 안전하게 다루고 싶을 때, HashiCorp Vault를 사용하면 시크릿을 중앙에서 관리하고 잡에 안전하게 전달할 수 있어요. GitLab CI/CD에서는 ID 토큰을 이용해 HashiCorp Vault에 인증할 수 있어요.
출처: 문서
본문
- Tier: Premium, Ultimate
- Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
CI/CD 잡에서 Vault 시크릿을 사용하려면 먼저 Vault 서버를 설정해야 해요. Vault 설정과 ID 토큰 인증에 대한 자세한 내용은 HashiCorp Vault로 시크릿 인증·읽기 튜토리얼을 참고해요.
아래 예시에서 vault.example.com은 여러분의 Vault 서버 URL로, gitlab.example.com은 여러분의 GitLab 인스턴스 URL로 바꿔 쓰면 돼요.
Vault 서버 설정하기
Vault 서버를 설정하는 방법은 이래요.
- 다음 명령으로 인증 방식을 활성화해요. 이 명령은 Vault 서버에 GitLab 인스턴스의 OIDC Discovery URL을 제공해서, Vault가 인증 시 공개 서명 키를 가져와 JSON Web Token(JWT)을 검증할 수 있게 해줘요.
$ vault auth enable jwt
$ vault write auth/jwt/config \
oidc_discovery_url="https://gitlab.example.com" \
bound_issuer="gitlab.example.com"
- Vault 서버에 정책(policy)을 설정해서 특정 경로와 작업에 대한 접근을 허용하거나 차단해요. 다음 예시는 운영 환경이 필요로 하는 시크릿 집합에 읽기 권한을 부여해요.
vault policy write myproject-production - <<EOF
# 'ops/data/production/*' 경로에 대한 읽기 전용 권한
path "ops/data/production/*" {
capabilities = [ "read" ]
}
EOF
- Vault 서버에 역할(role)을 설정해서 역할을 특정 프로젝트나 네임스페이스로 제한해요.
- Vault 서버 정보를 담을 CI/CD 변수를 만들어요.
VAULT_SERVER_URL: Vault 서버 URL, 예를 들어https://vault.example.com:8200예요.VAULT_AUTH_ROLE: 선택. 인증을 시도할 때 사용할 역할이에요. 역할을 지정하지 않으면 Vault는 인증 방식 설정 시 지정된 기본 역할을 사용해요.VAULT_AUTH_PATH: 선택. 인증 방식이 마운트된 경로로, 기본값은jwt예요.VAULT_NAMESPACE: 선택. 시크릿 읽기와 인증에 사용할 Vault Enterprise 네임스페이스예요. Vault(OS 버전)에서는 네임스페이스를 지정하지 않으면root(/) 네임스페이스가 사용돼요. Vault Open source에서는 이 설정이 무시돼요. HashiCorp Cloud Platform(HCP) Vault에서는 네임스페이스가 필수인데, HCP Vault는 기본적으로admin네임스페이스를 루트 네임스페이스로 사용해요. 예를 들어VAULT_NAMESPACE=admin처럼 설정하면 돼요.
서버 역할 설정하기
CI/CD 잡이 인증을 시도할 때 역할을 지정해요. 역할을 사용하면 여러 정책을 하나로 묶을 수 있어요. 인증이 성공하면 이 정책들이 생성된 Vault 토큰에 첨부돼요.
Bound claims은 JWT 클레임과 대조되는 미리 정의된 값이에요. bounded claims를 사용하면 특정 GitLab 사용자, 특정 프로젝트, 심지어 특정 Git 참조(ref)에서 실행되는 잡으로만 접근을 제한할 수 있어요. bounded claims는 필요한 만큼 여러 개 쓸 수 있지만, 인증이 성공하려면 모두 일치해야 해요.
bounded claims를 사용자 역할이나 보호 브랜치 같은 GitLab 기능과 결합하면 규칙을 특정 사용 사례에 맞게 조정할 수 있어요. 다음 예시에서는 운영 릴리스에 쓰는 이름 패턴과 일치하는 보호 태그(protected tag)에 대해 실행되는 잡만 인증을 허용해요.
$ vault write auth/jwt/role/myproject-production - <<EOF
{
"role_type": "jwt",
"policies": ["myproject-production"],
"token_explicit_max_ttl": 60,
"user_claim": "user_email",
"bound_audiences": "https://vault.example.com",
"bound_claims_type": "glob",
"bound_claims": {
"project_id": "42",
"ref_protected": "true",
"ref_type": "tag",
"ref": "auto-deploy-*"
}
}
EOF
역할은 항상 project_id나 namespace_id 같은 제공된 클레임 중 하나를 사용해서 특정 프로젝트나 네임스페이스로 제한해야 해요. 이런 제한 없이는 이 GitLab 인스턴스에서 생성된 어떤 JWT든 이 역할로 인증할 수 있어요.
ID 토큰 JWT 클레임 전체 목록은 HashiCorp Vault 시크릿 사용 튜토리얼에서 확인할 수 있어요.
생성되는 Vault 토큰의 속성(ttl, IP 주소 범위, 사용 횟수 등)도 지정할 수 있어요. 전체 옵션 목록은 Vault의 역할 생성 문서에서 확인할 수 있어요.
CI/CD 잡에서 Vault 시크릿 사용하기
잡에 ID 토큰이 하나 이상 정의되어 있으면 secrets 키워드가 자동으로 그 토큰을 사용해 Vault에 인증해요.
Vault 서버를 설정한 뒤 secrets:vault 키워드로 Vault에 저장된 시크릿을 사용하면 돼요.
job_using_vault:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
secrets:
DATABASE_PASSWORD:
vault: production/db/password@ops
token: $VAULT_ID_TOKEN
이 예시에서 각 요소의 의미를 살펴볼게요.
production/db는 시크릿 경로예요.password는 필드예요.ops는 시크릿 엔진이 마운트된 경로예요.production/db/password@ops는ops/data/production/db경로로 변환돼요.- 인증은
$VAULT_ID_TOKEN으로 해요.
GitLab이 Vault에서 시크릿을 가져오면 값은 임시 파일에 저장돼요. 이 파일의 경로는 type이 file인 변수처럼 DATABASE_PASSWORD라는 CI/CD 변수에 담겨요.
기본 동작을 덮어쓰려면 file 옵션을 명시적으로 false로 설정해요.
secrets:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
DATABASE_PASSWORD:
vault: production/db/password@ops
file: false
token: $VAULT_ID_TOKEN
이 예시에서는 시크릿 값을 담은 파일을 가리키는 대신, 시크릿 값이 DATABASE_PASSWORD 변수에 직접 들어가요.
시크릿 엔진 (Secrets engine)
GitLab Runner는 secrets:engine:name 키워드로 다양한 시크릿 엔진을 지원해요.
| Secrets engine | secrets:engine:name 값 |
세부 내용 |
|---|---|---|
| KV secrets engine - version 2 | kv-v2 |
엔진 유형을 명시적으로 지정하지 않으면 GitLab Runner가 기본으로 사용하는 엔진이 kv-v2예요. |
| KV secrets engine - version 1 | kv-v1 또는 generic |
|
| AWS secrets engine | generic |
|
| HashiCorp Vault Artifactory Secrets Plugin | generic |
JFrog Artifactory 서버 5.0.0 이상과 통신하며, 지정된 범위의 액세스 토큰을 동적으로 발급해요. |
다른 시크릿 엔진 사용하기
기본적으로 kv-v2 시크릿 엔진이 사용돼요. 다른 엔진을 쓰려면 설정의 vault 아래에 engine 섹션을 추가해요.
예를 들어 Artifactory용 시크릿 엔진과 경로를 설정하려면 이렇게 해요.
job_using_vault:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
secrets:
JFROG_TOKEN:
vault:
engine:
name: generic
path: artifactory
path: production/jfrog
field: access_token
file: false
이 예시에서는 field가 access_token인 artifactory/production/jfrog에서 시크릿 값을 가져와요.
문제 해결 (Troubleshooting)
자체 서명 인증서 오류: certificate signed by unknown authority
Vault 서버가 자체 서명 인증서를 사용하면 잡 로그에 다음 오류가 나타나요.
ERROR: Job failed (system failure): resolving secrets: initializing Vault service: preparing authenticated client: checking Vault server health: Get https://vault.example.com:8000/v1/sys/health?drsecondarycode=299&performancestandbycode=299&sealedcode=299&standbycode=299&uninitcode=299: x509: certificate signed by unknown authority
이 오류를 해결하는 방법은 두 가지예요.
- 자체 서명 인증서를 GitLab Runner 서버의 CA 저장소에 추가해요. Helm 차트로 GitLab Runner를 배포했다면 직접 GitLab Runner 이미지를 만들어야 해요.
VAULT_CACERT환경 변수로 GitLab Runner가 인증서를 신뢰하도록 설정해요. systemd로 GitLab Runner를 관리한다면 환경 변수 추가 방법을 참고해요. Helm 차트로 배포했다면 GitLab 접근용 사용자 지정 인증서 제공 방법을 참고하되, GitLab용 인증서 대신 Vault 서버용 인증서를 추가해야 해요. GitLab 인스턴스도 자체 서명 인증서를 쓴다면 둘 다 같은Secret에 추가할 수 있어요.values.yaml파일에 다음 줄을 추가해요.
## <SECRET_NAME>과 <VAULT_CERTIFICATE>를 모두
## 시크릿 생성 시 사용한 실제 값으로 바꿔주세요
certsSecretName: <SECRET_NAME>
envVars:
- name: VAULT_CACERT
value: "/home/gitlab-runner/.gitlab-runner/certs/<VAULT_CERTIFICATE>"
GitLab Development Kit(GDK)로 로컬에서 Vault 서버를 개발 모드로 실행하는 경우에도 이 오류가 나올 수 있어요. Vault 서버의 자체 서명 인증서를 시스템에 수동으로 신뢰하도록 요청하면 돼요. 이 샘플 튜토리얼이 macOS에서 하는 방법을 설명해요.
resolving secrets: secret not found: MY_SECRET 오류
GitLab이 Vault에서 시크릿을 찾지 못하면 다음 오류가 나올 수 있어요.
ERROR: Job failed (system failure): resolving secrets: secret not found: MY_SECRET
vault 값이 CI/CD 잡에 올바르게 설정되어 있는지 확인해요.
Vault CLI의 kv 명령으로 시크릿을 가져올 수 있는지 확인하면 CI/CD 설정의 vault 값 문법을 정하는 데 도움이 돼요. 예를 들어 시크릿을 가져오려면:
$ vault kv get -field=password -namespace=admin -mount=ops "production/db"
this-is-a-password
더 알아보기
Vault 시크릿을 더 능숙하게 다루려면 HashiCorp Vault 튜토리얼에서 ID 토큰 인증과 함께 Vault를 구성하는 전체 흐름을 따라 해보는 걸 추천해요. 또 secrets 키워드와 ID 토큰 문법도 함께 익혀두면 여러 시크릿 공급자에서 동일한 패턴을 쓸 수 있어요.