ID 토큰을 사용한 OpenID Connect
ID 토큰을 사용한 OpenID Connect (OIDC) 인증
ID 토큰은 GitLab CI/CD가 생성하는 JSON 웹 토큰(JWT)이에요. CI/CD 잡은 ID 토큰을 사용해 다음과 같은 서드파티 서비스와 OIDC 인증을 할 수 있어요.
예를 들어 ID 토큰으로 HashiCorp Vault에 인증하는 흐름은 이 다이어그램으로 요약할 수 있어요.
sequenceDiagram
participant GitLab as GitLab CI/CD
participant Runner as GitLab Runner
participant Vault as HashiCorp Vault
GitLab->>Runner: Generates an ID token (JWT) for the CI/CD job
Runner->>Vault: Runner authenticates with HashiCorp Vault using the token
Vault->>Vault: HashiCorp Vault verifies the token
Vault->>Vault: HashiCorp Vault checks bounded claims and attaches policies
Vault->>Runner: HashiCorp Vault returns the token
Runner->>Vault: Runner requests secrets from HashiCorp Vault
Vault->>Runner: Returns secrets
ID 토큰은 [secrets](/ci/yaml/#secrets) 키워드에서도 사용돼요.
GitLab Duo Agent Platform 플로우와 외부 에이전트도 실행 중에 서드파티 서비스로 인증하기 위해 id_tokens를 선언할 수 있어요.
출처: 문서
본문
CI/CD 잡에서 ID 토큰 구성하기
ID 토큰을 사용하려면 CI/CD 잡을 [id_tokens](/ci/yaml/#id_tokens) 키워드로 구성해요. 그런 다음 script, before_script, after_script 섹션에서 토큰을 사용할 수 있어요.
예를 들어:
job_with_id_tokens:
id_tokens:
FIRST_ID_TOKEN:
aud: https://first.service.com
SECOND_ID_TOKEN:
aud: https://second.service.com
script:
- first-service-authentication-script.sh $FIRST_ID_TOKEN
- second-service-authentication-script.sh $SECOND_ID_TOKEN
이 예시에서 두 토큰은 서로 다른 aud 클레임을 가져요. 서드파티 서비스는 자신의 바운드 오디언스와 일치하는 aud 클레임이 없는 토큰을 거부하도록 구성할 수 있어요. 이 기능으로 토큰이 인증할 수 있는 서비스 수를 줄여, 토큰이 손상되었을 때의 심각성을 낮출 수 있어요.
토큰 페이로드
각 ID 토큰에는 다음 표준 클레임이 포함돼요.
| 필드 | 설명 |
|---|---|
| iss | 토큰의 발급자(Issuer)로, GitLab 인스턴스의 도메인이에요(issuer 클레임). |
| sub | 토큰의 주체(subject claim). 기본값은 project_path:{group}/{project}:ref_type:{type}:ref:{branch_name}. 프로젝트 API로 프로젝트에 대해 구성할 수 있어요. sub 클레임은 잡이 환경을 지정할 때 ref_protected, 그리고 environment_protected, deployment_tier 같은 환경 관련 필드를 추가로 포함할 수 있어요. GitLab 18.7에서 도입. |
| aud | 토큰의 의도된 대상(audience claim). ID 토큰 구성에서 지정. 기본적으로 GitLab 인스턴스의 도메인. |
| exp | 만료 시간(expiration time claim). |
| nbf | 토큰이 유효해지는 시간(not before claim). |
| iat | JWT가 발급된 시간(issued at claim). |
| jti | 토큰의 고유 식별자(JWT ID claim). |
토큰에는 GitLab이 제공하는 커스텀 클레임도 포함돼요.
| 필드 | 시점 | 설명 |
|---|---|---|
| project_id | 항상 | 잡을 실행하는 프로젝트의 ID. 머지 리퀘스트 파이프라인에서는 소스 프로젝트의 ID. |
| project_path | 항상 | 잡을 실행하는 프로젝트의 경로. 머지 리퀘스트 파이프라인에서는 소스 프로젝트의 경로. |
| namespace_id | 항상 | 잡을 실행하는 프로젝트의 네임스페이스 ID. 머지 리퀘스트 파이프라인에서는 소스 프로젝트의 네임스페이스 ID. |
| namespace_path | 항상 | 잡을 실행하는 프로젝트의 네임스페이스 경로. 머지 리퀘스트 파이프라인에서는 소스 프로젝트의 네임스페이스 경로. |
| user_id | 항상 | 잡을 실행하는 사용자의 ID. |
| user_login | 항상 | 잡을 실행하는 사용자의 사용자 이름. |
| user_email | 항상 | 잡을 실행하는 사용자의 이메일. |
| user_access_level | 항상 | 잡을 실행하는 사용자의 접근 수준. |
| job_project_id | 항상 | 잡을 실행하는 프로젝트의 ID. ID로 프로젝트에 범위를 지정하는 데 사용. GitLab 18.4에서 도입. |
| job_project_path | 항상 | 잡을 실행하는 프로젝트의 경로. 경로로 프로젝트에 범위를 지정하는 데 사용. GitLab 18.4에서 도입. |
| job_namespace_id | 항상 | 잡을 실행하는 프로젝트의 네임스페이스 ID. ID로 그룹 또는 사용자 수준 네임스페이스에 범위를 지정하는 데 사용. GitLab 18.4에서 도입. |
| job_namespace_path | 항상 | 잡을 실행하는 프로젝트의 네임스페이스 경로. 경로로 그룹 또는 사용자 수준 네임스페이스에 범위를 지정하는 데 사용. GitLab 18.4에서 도입. |
| user_identities | 사용자 기본 설정 | 사용자의 외부 ID 목록. |
| pipeline_id | 항상 | 파이프라인의 ID. |
| pipeline_source | 항상 | 파이프라인 소스. |
| job_id | 항상 | 잡의 ID. |
| ref | 항상 | 잡의 Git ref. 머지 리퀘스트 파이프라인에서는 소스 브랜치 ref. |
| ref_type | 항상 | Git ref 유형, branch 또는 tag. |
| ref_path | 항상 | 잡의 정규화된 ref. 예: refs/heads/main. 머지 리퀘스트 파이프라인에서는 소스 브랜치 ref 경로. |
| ref_protected | 항상 | Git ref가 보호되면 true, 그렇지 않으면 false. |
| groups_direct | 사용자가 0~200개 그룹의 직접 멤버 | 사용자의 직접 멤버십 그룹 경로. 사용자가 200개가 넘는 그룹의 직접 멤버면 생략. 피처 플래그 ci_jwt_groups_direct가 GitLab 17.3에서 추가. 기본적으로 비활성화. |
| environment | 잡이 환경 지정 | 이 잡이 배포하는 환경. |
| environment_protected | 잡이 환경 지정 | 배포된 환경이 보호되면 true, 그렇지 않으면 false. |
| deployment_tier | 잡이 환경 지정 | 잡이 지정하는 환경의 배포 티어. |
| environment_action | 잡이 환경 지정 | 잡에 지정된 환경 동작(environment:action). |
| runner_id | 항상 | 잡을 실행하는 러너의 ID. |
| runner_environment | 항상 | 잡이 사용하는 러너 유형. gitlab-hosted 또는 self-hosted. |
| sha | 항상 | 잡의 커밋 SHA. |
| ci_config_ref_uri | 항상 | 최상위 파이프라인 정의의 ref 경로. 예: gitlab.example.com/my-group/my-project//.gitlab-ci.yml@refs/heads/main. 파이프라인 정의가 같은 프로젝트에 있지 않으면 null. |
| ci_config_sha | 항상 | ci_config_ref_uri의 Git 커밋 SHA. 파이프라인 정의가 같은 프로젝트에 있지 않으면 null. |
| project_visibility | 항상 | 파이프라인이 실행되는 프로젝트의 가시성. internal, private 또는 public. |
| job_source | 항상 | 잡 소스. GitLab 18.9에서 도입. |
| job_config | 정책에 의해 트리거된 잡 | 잡의 출처에 대한 메타데이터. 정책 잡의 경우 정책 구성의 sha와 url 포함. GitLab 18.9에서 도입. |
다음은 ID 토큰 페이로드 예시예요.
{
"namespace_id": "72",
"namespace_path": "my-group",
"project_id": "20",
"project_path": "my-group/my-project",
"user_id": "1",
"user_login": "sample-user",
"user_email": "[email protected]",
"user_identities": [
{"provider": "github", "extern_uid": "2435223452345"},
{"provider": "bitbucket", "extern_uid": "john.smith"}
],
"pipeline_id": "574",
"pipeline_source": "push",
"job_id": "302",
"ref": "feature-branch-1",
"ref_type": "branch",
"ref_path": "refs/heads/feature-branch-1",
"ref_protected": "false",
"groups_direct": ["mygroup/mysubgroup", "myothergroup/myothersubgroup"],
"environment": "test-environment2",
"environment_protected": "false",
"deployment_tier": "testing",
"environment_action": "start",
"job_source": "push",
"job_config": {
"url": "https://gitlab.example.com/my-group/my-policy-project/-/blob/ab035e64eca9a7a85bd62e485d3593f52a2804ac/.gitlab/security-policies/policy.yml",
"sha": "ab035e64eca9a7a85bd62e485d3593f52a2804ac"
},
"runner_id": 1,
"runner_environment": "self-hosted",
"sha": "714a629c0b401fdce83e847fc9589983fc6f46bc",
"project_visibility": "public",
"ci_config_ref_uri": "gitlab.example.com/my-group/my-project//.gitlab-ci.yml@refs/heads/main",
"ci_config_sha": "714a629c0b401fdce83e847fc9589983fc6f46bc",
"jti": "235b3a54-b797-45c7-ae9a-f72d7bc6ef5b",
"iss": "https://gitlab.example.com",
"iat": 1681395193,
"nbf": 1681395188,
"exp": 1681398793,
"sub": "project_path:my-group/my-project:ref_type:branch:ref:feature-branch-1",
"aud": "https://vault.example.com"
}
ID 토큰은 RS256으로 인코딩되고 전용 프라이빗 키로 서명돼요. 토큰의 만료 시간은 잡의 타임아웃이 지정된 경우 그 값으로, 지정되지 않았으면 5분으로 설정돼요.
클라우드 신뢰 정책에서 ID 토큰 클레임 사용하기
OIDC ID 제공자로 GitLab과 연합하는 클라우드 제공자는 신뢰 정책의 조건 키로 위 클레임을 검증할 수 있어요.
신뢰 정책을 작성할 때, 클라우드 제공자와 GitLab 제품이 지원한다면 sub 같은 경로 기반 클레임과 함께 namespace_id, project_id 같은 안정적이고 고유한 식별자를 포함해요. project_id는 전역적으로 고유하며 프로젝트 수명 동안 동일하게 유지돼요. namespace_id는 프로젝트가 현재 네임스페이스에 있는 동안 안정적이에요. 두 식별자 모두 경로와 독립적이므로, 이들을 포함한 신뢰 정책은 그룹이나 프로젝트 이름 변경 같은 경로 변경의 영향을 받지 않아요.
GitLab.com의 AWS에서 다음 GitLab 클레임이 gitlab.com OIDC ID 제공자의 조건 키로 사용 가능해요.
namespace_idproject_iduser_iduser_loginuser_emailuser_access_levelref_protectedpipeline_source
이 조건 키는 gitlab.com OIDC ID 제공자에서만 사용할 수 있어요. GitLab Self-Managed나 GitLab Dedicated에서는 사용할 수 없으며, 거기서는 sub 클레임만 AWS 조건 키로 지원돼요.
사용자가 변경할 수 있으므로 user_login이나 user_email을 유일한 조건으로 의존하지 마세요. GitLab ID 제공자에 대해 AWS가 게시한 조건 키의 정확한 세트를 확인해요.
sub, namespace_id, project_id를 사용하는 완전한 AWS 신뢰 정책 예시는 AWS에서 OpenID Connect 구성하기를 참고해요. HashiCorp Vault는 바운드 클레임을 참고해요.
문제 해결
400: missing token 상태 코드
이 오류는 ID 토큰에 필요한 기본 구성 요소 중 하나 이상이 없거나 기대한 대로 구성되지 않았음을 나타내요.
문제를 찾으려면 관리자가 인스턴스의 exceptions_json.log에서 실패한 특정 메서드에 대한 세부 정보를 찾을 수 있어요.
GitLab::Ci::Jwt::NoSigningKeyError
exceptions_json.log 파일의 이 오류는 서명 키가 데이터베이스에 없어서 토큰을 생성할 수 없을 가능성이 높아요. 이것이 문제인지 확인하려면 인스턴스의 PostgreSQL 터미널에서 다음 쿼리를 실행해요.
SELECT encrypted_ci_jwt_signing_key FROM application_settings;
반환된 값이 비어 있으면 다음 Rails 스니펫을 사용해 새 키를 생성하고 내부적으로 교체해요.
key = OpenSSL::PKey::RSA.new(2048).to_pem
ApplicationSetting.find_each do |application_setting|
application_setting.update(ci_jwt_signing_key: key)
end
401: unauthorized 상태 코드
이 오류는 인증 요청이 실패했음을 나타내요. GitLab 파이프라인에서 외부 서비스로 OpenID Connect(OIDC) 인증을 사용할 때 401 Unauthorized 오류는 여러 일반적인 이유로 발생할 수 있어요.
$CI_JOB_JWT_V2같은 더 이상 사용되지 않는 토큰을 ID 토큰 대신 사용했어요. 자세한 내용은 이전 버전의 JSON 웹 토큰은 더 이상 사용되지 않음을 참고해요..gitlab-ci.yml파일과 외부 서비스의 OIDC Identity Provider 구성 사이에provider_name값이 일치하지 않아요.- GitLab이 발급한 ID 토큰의
aud(audience) 클레임과 외부 서비스가 기대하는 값이 빠졌거나 일치하지 않아요. - GitLab CI/CD 잡에서
id_tokens:블록을 활성화하거나 구성하지 않았어요.
오류를 해결하려면 잡 안에서 토큰을 디코딩해요.
echo $OIDC_TOKEN | cut -d '.' -f2 | base64 -d | jq .
다음을 확인해요.
aud(audience)가 예상 대상(예: 외부 서비스의 URL)과 일치하는지.sub(subject)가 서비스의 Identity Provider 설정에 매핑되어 있는지.preferred_username은 GitLab ID 토큰에 기본적으로 존재하지 않아요.
오류: ID token issuance is disabled
CI/CD 잡이 ID 토큰을 요청할 때 이 오류를 받을 수 있어요.
ID token issuance is disabled in CI because this project's path was previously used by a different project.
GitLab은 구성된 sub 클레임에 다른 프로젝트가 이전에 사용했던 경로가 있는 project_path가 포함되어 있으면 ID 토큰 발급을 차단해요. 이 제한은 새 프로젝트가 이전 프로젝트에 속한 외부 신뢰 정책을 상속하는 것을 방지해요.
오류를 해결하려면 projects API로 project_id를 첫 번째 값으로 ci_id_token_sub_claim_components를 설정해요.
{
"ci_id_token_sub_claim_components": ["project_id", "ref_type", "ref"]
}
결과 sub 클레임은 다음 형식이에요.
project_id:<id>:ref_type:<type>:ref:<ref>
각 외부 서비스 신뢰 정책을 새 주체에 맞게 업데이트해요. 경로 기록이 예상과 다르면 인스턴스 관리자에게 검토를 요청해요.
클라우드 서비스 신뢰 정책 지침은 프로젝트 ID를 subject로 사용하기를 참고해요.
더 알아보기
ID 토큰은 시크릿 제공자 인증의 핵심이에요. HashiCorp Vault와 함께 사용하는 전체 튜토리얼은 HashiCorp Vault 튜토리얼을, 클라우드 서비스 인증은 클라우드 서비스 문서에서 확인할 수 있어요.