HashiCorp Vault를 사용하여 인증 및 비밀 정보 읽기
제공: GitLab.com, Self-managed, GitLab Dedicated
CI_JOB_JWT
로의 인증은 GitLab 15.9에서 사용 중단되었으며 해당 토큰은 GitLab 17.0에서 삭제 예정입니다. 대신 HashiCorp Vault로 인증하기 위해 ID 토큰을 사용하십시오. 이 페이지에서 설명된 대로.이 튜토리얼에서는 GitLab CI/CD에서 HashiCorp의 Vault로의 인증, 구성, 및 비밀 정보 읽기를 보여줍니다.
전제 조건
이 튜토리얼은 GitLab CI/CD와 Vault에 익숙하다고 가정합니다.
따라오기 위해서는 다음이 있어야 합니다:
- GitLab에 계정.
- 인증 구성 및 역할 및 정책 생성을 위한 실행 중인 Vault 서버에 액세스 (최소 v1.2.0). HashiCorp Vaults의 경우 Open Source 또는 Enterprise 버전이 될 수 있습니다.
아래의
vault.example.com
URL을 귀하의 Vault 서버 URL로, gitlab.example.com
을 귀하의 GitLab 인스턴스 URL로 교체해야 합니다.작동 방식
ID 토큰은 제3자 서비스와의 OIDC 인증을 위해 사용되는 JSON Web Token (JWT)입니다. 작업에 ID 토큰이 하나 이상 정의된 경우, secrets
키워드는 자동으로 해당 토큰을 사용하여 Vault로 인증합니다.
다음 필드가 JWT에 포함됩니다:
필드 | 시점 | 설명 |
---|---|---|
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_type
| 항상 | Git 참조 유형, branch 또는 tag 중 하나
|
ref_path
| 항상 | 작업의 완전히 지정된 참조. 예: refs/heads/main . GitLab 16.0에서 도입
|
ref_protected
| 항상 | 이 Git 참조가 보호되어 있는 경우 true , 그렇지 않으면 false
|
environment
| 작업이 환경을 지정 | 이 작업이 지정하는 환경 (GitLab 13.9에 도입) |
environment_protected
| 작업이 환경을 지정 | 지정된 환경이 보호되어 있는 경우 true , 그렇지 않으면 false (GitLab 13.9에 도입)
|
deployment_tier
| 작업이 환경을 지정 | 이 작업이 지정하는 환경의 배포 티어 (GitLab 15.2에 도입) |
environment_action
| 작업이 환경을 지정 | 작업에서 지정된 환경 동작(environment:action ) (GitLab 16.5에 도입)
|
예시 JWT 페이로드:
{
"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",
"environment": "production",
"environment_protected": "true",
"environment_action": "start"
}
이 JWT는 RS256으로 인코딩되고 별도의 개인 키로 서명됩니다. 해당 토큰의 만료 시간은 작업의 타임아웃으로 설정되거나 그렇지 않은 경우 5분으로 설정됩니다. 이 토큰 서명에 사용되는 키는 언제든지 변경될 수 있습니다. 이 경우 작업을 재시도하면 최신 서명 키를 사용하여 새 JWT를 생성합니다.
귀하의 Vault 서버가 JWT 인증 방법을 허용하도록 구성된 경우, 이 JWT를 Vault 서버와의 인증에 사용할 수 있습니다. 귀하의 GitLab 인스턴스의 기본 URL을 Vault 서버의 oidc_discovery_url
로 제공할 수 있습니다. 서버는 그런 다음 귀하의 인스턴스로부터 토큰을 유효성 검사를 위한 키를 검색할 수 있습니다.
Vault에서 역할을 구성하는 경우 bound claims를 사용하여 JWT 클레임과 일치시켜 각 CI/CD 작업이 액세스할 수 있는 비밀 정보를 제한할 수 있습니다.
Vault와 통신하기 위해서는 CLI 클라이언트를 사용하거나 (curl
또는 다른 클라이언트를 사용하여) API 요청을 수행할 수 있습니다.
예시
예를 들어, 스테이징과 프로덕션 데이터베이스의 비밀번호가 http://vault.example.com:8200
에서 실행 중인 보르트 서버에 저장되어 있다고 가정해봅시다. 스테이징 비밀번호는 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
보르트 서버를 구성하려면 먼저 JWT 인증 방법을 활성화하세요.
$ 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
이라는 이름의 역할하나:
$ vault write auth/jwt/role/myproject-staging - <<EOF
{
"role_type": "jwt",
"policies": ["myproject-staging"],
"token_explicit_max_ttl": 60,
"user_claim": "user_email",
"bound_claims": {
"project_id": "22",
"ref": "master",
"ref_type": "branch"
}
}
EOF
그리고 프로덕션용으로 myproject-production
이라는 이름의 역할하나:
$ vault write auth/jwt/role/myproject-production - <<EOF
{
"role_type": "jwt",
"policies": ["myproject-production"],
"token_explicit_max_ttl": 60,
"user_claim": "user_email",
"bound_claims_type": "glob",
"bound_claims": {
"project_id": "22",
"ref_protected": "true",
"ref_type": "branch",
"ref": "auto-deploy-*"
}
}
EOF
이 예시에서는 bound claims를 사용하여 지정된 클레임 값과 일치하는 JWT만 인증하는 방법을 지정합니다.
이를 보호된 브랜치와 함께 사용하면 누가 인증하고 비밀을 읽을 수 있는지 제한할 수 있습니다.
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
만 사용되면 해당 네임스페이스의 모든 프로젝트가 허용됩니다. -
namespace_id
와project_id
둘 다 사용되면, Vault는 먼저 프로젝트의 네임스페이스가namespace_id
에 있는지 확인합니다. 그렇지 않으면 프로젝트가project_id
에 있는지 확인합니다.
token_explicit_max_ttl
은 Vault에서 성공적으로 인증한 토큰의 하드 수명을 60초로 설정함을 나타냅니다.
user_claim
은 Vault에서 성공적인 로그인 후 생성된 Identity alias의 이름을 지정합니다.
bound_claims_type
은 bound_claims
값의 해석을 구성합니다. glob
로 설정할 경우, 값은 glob으로 해석되며, *
는 모든 문자와 일치합니다.
위의 테이블에서 나열된 클레임 필드들은 Vault의 정책 경로 템플릿화를 위해 JWT 인증의 엑세서 이름을 사용하여 접근할 수도 있습니다(여기 참조).
메타데이터 필드인 project_path
를 사용하는 정책 템플릿 예시:
path "secret/data/{{identity.entity.aliases.ACCESSOR_NAME.metadata.project_path}}/staging/*" {
capabilities = [ "read" ]
}
위 템플릿에 따른 역할 예시로, claim_mappings
구성을 통해 클레임 필드 project_path
를 메타데이터 필드로 매핑:
{
"role_type": "jwt",
...
"claim_mappings": {
"project_path": "project_path"
}
}
모든 옵션의 전체 디렉터리은 Vault의 역할 생성 문서를 참조하세요.
이제 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에서 프로젝트용 다음과 같은 CI/CD 변수를 생성하여 보르트 서버에 대한 자세한 정보를 제공하세요:
-
VAULT_SERVER_URL
- 보르트 서버의 URL, 예:https://vault.example.com:8200
. -
VAULT_AUTH_ROLE
- 선택 사항. 인증 시 사용할 역할. 역할이 지정되지 않으면 Vault는 인증 방법이 구성될 때 지정한 기본 역할을 사용합니다. -
VAULT_AUTH_PATH
- 선택 사항. 인증 방법이 마운트된 경로. 기본값은jwt
입니다. -
VAULT_NAMESPACE
- 선택 사항. 비밀번호 읽기 및 인증에 사용할 Vault 엔터프라이즈 네임스페이스. 네임스페이스가 지정되지 않으면 Vault는 루트(/
) 네임스페이스를 사용합니다. 이 설정은 Vault 오픈 소스에서 무시됩니다.
기본 브랜치에서 실행될 경우 다음 작업은 secret/myproject/staging/
아래의 비밀을 읽지만, secret/myproject/production/
아래의 비밀은 읽을 수 없습니다:
job_with_secrets:
id_tokens:
VAULT_ID_TOKEN:
aud: https://example.vault.com
secrets:
STAGING_DB_PASSWORD:
vault: secret/myproject/staging/db/password@secrets # $VAULT_ID_TOKEN을 사용하여 인증
script:
- access-staging-db.sh --token $STAGING_DB_PASSWORD
이 예시에서:
-
@secrets
- 활성화된 Secrets Engine이 있는 보르트 이름. -
secret/myproject/staging/db
- Vault의 중요 정보가 위치한 경로. -
password
- 참조되는 비밀번호 필드입니다.
Vault 시크릿의 토큰 접근 제한
Vault 보호 및 GitLab 기능을 사용하여 Vault 시크릿의 ID 토큰 접근을 제어할 수 있습니다. 예를 들어, 다음과 같이 토큰을 제한할 수 있습니다:
- 특정 그룹을 위해 Vault bound claims 및
group_claim
을 사용합니다. - 특정 사용자의
user_login
및user_email
을 기반으로 Vault bound claims의 값을 하드 코딩합니다. - 인증 후 토큰이 만료되도록
token_explicit_max_ttl
에서 토큰의 TTL에 대한 Vault 시간 제한을 설정합니다. - GitLab 보호 브랜치에 JWT를 스코핑하여 프로젝트 사용자의 하위 집합에 제한된 브랜치에 적용합니다.
- 프로젝트 사용자의 하위 집합에 제한된 GitLab 보호 태그에 JWT를 스코핑합니다.