GitLab CI/CD에서 HashiCorp Vault 비밀 사용하기
CI_JOB_JWT
로 인증하는 것은 GitLab 15.9에서 사용 중단되었으며토큰은 GitLab 18.0에서 제거될 예정입니다. 대신
ID 토큰을 사용하여 HashiCorp Vault에 인증하세요.
이 페이지에서 설명된 대로 진행하면 됩니다.
JWT에
aud
클레임이 포함된 경우. aud
클레임은 단일 문자열 또는 문자열 목록이 될 수 있습니다.이 튜토리얼에서는 GitLab CI/CD에서 HashiCorp Vault로 인증, 구성 및 비밀을 읽는 방법을 보여줍니다.
사전 요구 사항
이 튜토리얼은 GitLab CI/CD 및 Vault에 익숙하다고 가정합니다.
따라서 다음이 필요합니다:
- GitLab 계정.
- 인증을 구성하고 역할 및 정책을 생성하기 위해 실행 중인 Vault 서버에 대한 액세스 (최소 v1.2.0).
HashiCorp Vault의 경우 오픈 소스 또는 엔터프라이즈 버전일 수 있습니다.
vault.example.com
URL을 Vault 서버의 URL로 교체해야 하며,gitlab.example.com
을 GitLab 인스턴스의 URL로 교체해야 합니다.작동 방식
ID 토큰은 제3자 서비스와의 OIDC 인증에 사용되는 JSON 웹 토큰(JWT)입니다.
작업에 적어도 하나의 ID 토큰이 정의되어 있으면 secrets
키워드는 자동으로 해당 토큰을 사용하여 Vault에 인증합니다.
JWT에 포함된 필드는 다음과 같습니다:
Field | When | Description |
---|---|---|
jti |
항상 | 이 토큰의 고유 식별자 |
iss |
항상 | 발급자, GitLab 인스턴스의 도메인 |
iat |
항상 | 발급된 시간 |
nbf |
항상 | 유효하지 않은 시간 |
exp |
항상 | 만료 시간 |
sub |
항상 | 주제(작업 ID) |
namespace_id |
항상 | ID로 그룹 또는 사용자 수준 네임스페이스에 범위를 설정하는 데 사용 |
namespace_path |
항상 | 경로로 그룹 또는 사용자 수준 네임스페이스에 범위를 설정하는 데 사용 |
project_id |
항상 | ID로 프로젝트에 범위를 설정하는 데 사용 |
project_path |
항상 | 경로로 프로젝트에 범위를 설정하는 데 사용 |
user_id |
항상 | 작업을 실행하는 사용자의 ID |
user_login |
항상 | 작업을 실행하는 사용자의 사용자 이름 |
user_email |
항상 | 작업을 실행하는 사용자의 이메일 |
pipeline_id |
항상 | 이 파이프라인의 ID |
pipeline_source |
항상 | 파이프라인 소스 |
job_id |
항상 | 이 작업의 ID |
ref |
항상 | 이 작업의 Git ref |
ref_type |
항상 | Git ref 유형, branch 또는 tag 중 하나 |
ref_path |
항상 | 작업을 위한 완전한 자격을 갖춘 ref. 예를 들어, refs/heads/main . 소개됨 GitLab 16.0에서. |
ref_protected |
항상 | 이 Git ref가 보호된 경우 true , 그렇지 않으면 false
|
environment |
작업이 환경을 지정하는 경우 | 이 작업이 지정하는 환경 |
groups_direct |
사용자가 0에서 200개의 그룹의 직접 구성원인 경우 | 사용자의 직접 구성원 그룹의 경로. 사용자가 200개 이상의 그룹의 직접 구성원인 경우 생략됩니다. (소개됨 GitLab 16.11에서). |
environment_protected |
작업이 환경을 지정하는 경우 | 지정된 환경이 보호된 경우 true , 그렇지 않으면 false
|
deployment_tier |
작업이 환경을 지정하는 경우 | 지정된 환경의 배포 계층 (소개됨 GitLab 15.2에서) |
environment_action |
작업이 환경을 지정하는 경우 | 작업에 지정된 환경 작업 (environment:action ). (소개됨 GitLab 16.5에서) |
Example JWT payload:
{
"jti": "c82eeb0c-5c6f-4a33-abf5-4c474b92b558",
"iss": "gitlab.example.com",
"iat": 1585710286,
"nbf": 1585798372,
"exp": 1585713886,
"sub": "job_1212",
"namespace_id": "1",
"namespace_path": "mygroup",
"project_id": "22",
"project_path": "mygroup/myproject",
"user_id": "42",
"user_login": "myuser",
"user_email": "myuser@example.com",
"pipeline_id": "1212",
"pipeline_source": "web",
"job_id": "1212",
"ref": "auto-deploy-2020-04-01",
"ref_type": "branch",
"ref_path": "refs/heads/auto-deploy-2020-04-01",
"ref_protected": "true",
"groups_direct": ["mygroup/mysubgroup", "myothergroup/myothersubgroup"],
"environment": "production",
"environment_protected": "true",
"environment_action": "start"
}
JWT는 RS256을 사용하여 인코딩되고 전용 개인 키로 서명됩니다.
토큰의 만료 시간은 지정된 경우 작업의 타임아웃으로 설정되며, 그렇지 않으면 5분으로 설정됩니다.
이 토큰을 서명하는 데 사용되는 키는 사전 통보 없이 변경될 수 있습니다. 이 경우 작업을 다시 시도하면 현재 서명 키를 사용하여 새로운 JWT가 생성됩니다.
이 JWT를 사용하여 JWT 인증 방법을 허용하도록 구성된 Vault 서버에 인증할 수 있습니다.
GitLab 인스턴스의 기본 URL(예: https://gitlab.example.com
)을 Vault 서버에 oidc_discovery_url
로 제공하십시오.
서버는 그러면 귀하의 인스턴스에서 토큰을 검증하기 위한 키를 검색할 수 있습니다.
Vault에서 역할을 구성할 때 바인딩된 클레임을 사용하여 JWT 클레임과 일치시켜
각 CI/CD 작업이 액세스할 수 있는 비밀을 제한할 수 있습니다.
Vault와 통신하려면 CLI 클라이언트를 사용하거나 API 요청을 수행할 수 있습니다(예: curl
또는 다른 클라이언트 사용).
예제
경고:
JWT는 자격 증명으로, 리소스에 대한 접근을 부여할 수 있습니다.
이들을 붙여넣는 위치에 주의하세요!
예를 들어, http://vault.example.com:8200
에서 실행 중인 Vault 서버에
스테이징 및 프로덕션 데이터베이스의 비밀번호가 저장되어 있다고 가정해 보겠습니다.
스테이징 비밀번호는 pa$$w0rd
이며, 프로덕션 비밀번호는 real-pa$$w0rd
입니다.
$ vault kv get -field=password secret/myproject/staging/db
pa$$w0rd
$ vault kv get -field=password secret/myproject/production/db
real-pa$$w0rd
Vault 서버를 구성하려면 다음으로 JWT Auth 방법을 활성화합니다:
$ vault auth enable jwt
Success! Enabled jwt auth method at: jwt/
그런 다음 이 비밀들을 읽을 수 있는 정책을 만듭니다(각 비밀마다 하나씩):
$ vault policy write myproject-staging - <<EOF
# 정책 이름: myproject-staging
#
# 'secret/myproject/staging/*' 경로에 대한 읽기 전용 권한
path "secret/myproject/staging/*" {
capabilities = [ "read" ]
}
EOF
Success! Uploaded policy: myproject-staging
$ vault policy write myproject-production - <<EOF
# 정책 이름: myproject-production
#
# 'secret/myproject/production/*' 경로에 대한 읽기 전용 권한
path "secret/myproject/production/*" {
capabilities = [ "read" ]
}
EOF
Success! Uploaded policy: myproject-production
JWT와 이 정책을 연결하는 역할도 필요합니다.
예를 들어, myproject-staging
이라는 이름의 스테이징 역할이 있습니다.
바운드 클레임은
정확히 22
라는 ID를 가진 프로젝트의 main
브랜치에서만 정책을 사용할 수 있도록 설정됩니다:
$ vault write auth/jwt/role/myproject-staging - <<EOF
{
"role_type": "jwt",
"policies": ["myproject-staging"],
"token_explicit_max_ttl": 60,
"user_claim": "user_email",
"bound_audiences": "https://vault.example.com",
"bound_claims": {
"project_id": "22",
"ref": "main",
"ref_type": "branch"
}
}
EOF
그리고 myproject-production
이라는 이름의 프로덕션 역할이 있습니다.
이 역할의 bound_claims
섹션은 auto-deploy-*
패턴과 일치하는
보호된 브랜치만 비밀에 접근할 수 있도록 허용합니다.
$ 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": "22",
"ref_protected": "true",
"ref_type": "branch",
"ref": "auto-deploy-*"
}
}
EOF
보호된 브랜치와 결합하여,
누가 인증할 수 있고 비밀을 읽을 수 있는지를 제한할 수 있습니다.
JWT에 포함된 클레임 중 어떤 것도 바운드 클레임 값 목록과 일치시킬 수 있습니다.
예를 들어:
"bound_claims": {
"user_login": ["alice", "bob", "mallory"]
}
"bound_claims": {
"ref": ["main", "develop", "test"]
}
"bound_claims": {
"namespace_id": ["10", "20", "30"]
}
"bound_claims": {
"project_id": ["12", "22", "37"]
}
-
namespace_id
만 사용되는 경우, 해당 네임스페이스의 모든 프로젝트가 허용됩니다.
중첩된 프로젝트는 포함되지 않으므로 필요하다면 그들의 네임스페이스 ID도 목록에 추가해야 합니다. -
namespace_id
와project_id
를 모두 사용하는 경우, Vault는 먼저
프로젝트의 네임스페이스가namespace_id
에 있는지 확인한 다음,
프로젝트가project_id
에 있는지 확인합니다.
token_explicit_max_ttl
은
성공적인 인증 후 Vault에 의해 발급된 토큰이 60초의 하드 생명 주기를 갖도록 지정합니다.
user_claim
은
성공적인 로그인 후 Vault에 의해 생성된 Identity 별칭의 이름을 지정합니다.
bound_claims_type
은
bound_claims
값의 해석을 구성합니다. glob
으로 설정하면, 값들이 glob으로 해석되며
*
는 임의의 수의 문자를 매치합니다.
위의 표에 나열된 클레임 필드는
Vault의 정책 경로 템플릿화에도 접근할 수 있습니다.
JWT 인증의 접근자 이름을 사용하여 이를 수행합니다.
마운트 접근자 이름
(ACCESSOR_NAME
, 아래 예시)을 얻으려면 vault auth list
를 실행하세요.
명명된 메타데이터 필드 project_path
를 사용하는 정책 템플릿 예시:
path "secret/data/{{identity.entity.aliases.ACCESSOR_NAME.metadata.project_path}}/staging/*" {
capabilities = [ "read" ]
}
위의 템플릿화된 정책을 지원하기 위한 역할 예시로,
클레임 필드 project_path
를 claim_mappings
구성을 통해 메타데이터 필드로 매핑합니다:
{
"role_type": "jwt",
...
"claim_mappings": {
"project_path": "project_path"
}
}
전체 옵션 목록은 Vault의 Create Role 문서를 참조하세요.
경고:
항상 제공된 클레임 중 하나를 사용하여 역할을 프로젝트 또는 네임스페이스로 제한하세요
(예: project_id
또는 namespace_id
). 그렇지 않으면 이 인스턴스에서 생성된 모든 JWT가
이 역할을 사용하여 인증될 수 있습니다.
이제 JWT 인증 방법을 구성합니다:
$ vault write auth/jwt/config \
oidc_discovery_url="https://gitlab.example.com" \
bound_issuer="https://gitlab.example.com"
bound_issuer
은
오직 발급자(즉, iss
클레임)이 gitlab.example.com
으로 설정된 JWT만
이 방법을 사용해 인증할 수 있도록 지정하며,
oidc_discovery_url
(https://gitlab.example.com
)은 토큰을 검증하는 데 사용되어야 합니다.
사용 가능한 설정 옵션의 전체 목록은 Vault의 API 문서를 참조하세요.
GitLab에서, 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는 루트(/
) 네임스페이스를 사용합니다.
이 설정은 Vault Open Source에서는 무시됩니다.
기본 브랜치에서 실행될 때, 다음 작업은 secret/myproject/staging/
아래의 비밀을 읽을 수 있지만,
secret/myproject/production/
아래의 비밀은 읽을 수 없습니다:
job_with_secrets:
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
secrets:
STAGING_DB_PASSWORD:
vault: secret/myproject/staging/db/password@secrets # $VAULT_ID_TOKEN을 사용하여 인증
script:
- access-staging-db.sh --token $STAGING_DB_PASSWORD
이 예제에서:
-
id_tokens
- OIDC 인증에 사용되는 JSON 웹 토큰(JWT).aud
클레임은
Vault JWT 인증 방법의bound_audiences
매개변수와 일치하도록 설정됩니다. -
@secrets
- 비밀 엔진이 활성화된 Vault 이름입니다. -
secret/myproject/staging/db
- Vault 내 비밀의 경로 위치입니다. -
password
- 참조된 비밀에서 가져올 필드입니다.
Vault 비밀에 대한 토큰 액세스 제한
Vault 보호 및 GitLab 기능을 사용하여 Vault 비밀에 대한 ID 토큰 액세스를 제어할 수 있습니다.
예를 들어, 다음과 같이 토큰을 제한할 수 있습니다:
-
특정 ID 토큰
aud
클레임에 대해 Vault 바운드 청중 사용하기. -
특정 그룹에 대해
group_claim
을 사용하는 Vault 바운드 클레임 사용하기. -
특정 사용자의
user_login
및user_email
에 기반하여 Vault 바운드 클레임의 값을 하드코딩하기. -
토큰의 TTL에 대해
token_explicit_max_ttl
에서 지정된 Vault 시간 제한 설정하기, 여기서 토큰은 인증 후 만료됩니다. -
JWT를 GitLab 보호된 브랜치에 스코프 한정하기, 이는 프로젝트 사용자 집합에 제한됩니다.
-
JWT를 GitLab 보호된 태그에 스코프 한정하기, 이는 프로젝트 사용자 집합에 제한됩니다.
문제 해결
비밀 제공자를 찾을 수 없습니다. CI/CD 변수 확인 후 다시 시도하세요.
메시지
HashiCorp Vault에 접근하도록 구성된 작업을 시작할 때 이 오류가 발생할 수 있습니다:
비밀 제공자를 찾을 수 없습니다. CI/CD 변수를 확인 후 다시 시도하세요.
필요한 변수가 정의되지 않았기 때문에 작업을 생성할 수 없습니다:
VAULT_SERVER_URL