ID 토큰을 사용한 OpenID Connect (OIDC) 인증
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
ID 토큰은 GitLab CI/CD에서 생성하는 JSON 웹 토큰(JWT)입니다. 예를 들어 HashiCorp Vault로 인증하는 데 ID 토큰을 사용하는 흐름은 다음 다이어그램으로 요약됩니다: ID 토큰은 secrets 키워드에서도 사용됩니다.
히스토리
- GitLab 15.7에서 도입.
ID 토큰은 GitLab CI/CD에서 생성하는 JSON 웹 토큰(JWT)입니다. CI/CD 잡은 다음을 포함한 서드파티 서비스와의 OIDC 인증에 ID 토큰을 사용할 수 있습니다:
예를 들어 HashiCorp Vault로 인증하는 데 ID 토큰을 사용하는 흐름은 다음 다이어그램으로 요약됩니다:
소스 코드 보기
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</code></pre></details></div>
ID 토큰은 secrets 키워드에서도 사용됩니다.
CI/CD 잡에서 ID 토큰 구성#
ID 토큰을 사용하려면 id_tokens 키워드로 CI/CD 잡을 구성합니다.
그런 다음 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
토큰의 발급자, GitLab 인스턴스의 도메인("issuer" 클레임).
sub
토큰의 주제("subject" 클레임). 기본값은 project_path:{group}/{project}:ref_type:{type}:ref:{branch_name}. 프로젝트 API로 프로젝트에 대해 구성할 수 있습니다. sub 클레임은 ref_protected와 같은 추가 필드와 잡이 환경을 지정할 때 environment_protected 및 deployment_tier와 같은 환경 관련 필드를 포함할 수 있습니다. GitLab 18.7에서 도입됨.
aud
토큰의 의도된 대상("audience" 클레임). ID 토큰 구성에서 지정됩니다. 기본적으로 GitLab 인스턴스의 도메인.
exp
만료 시간("expiration time" 클레임).
nbf
토큰이 유효해지는 시간("not before" 클레임).
iat
JWT가 발급된 시간("issued at" 클레임).
jti
토큰의 고유 식별자("JWT ID" 클레임).
토큰에는 GitLab에서 제공하는 커스텀 클레임도 포함됩니다:
필드
조건
설명
project_id
항상
잡을 실행하는 프로젝트의 ID. 머지 리퀘스트 파이프라인에서 이것은 소스 프로젝트의 ID입니다.
project_path
항상
잡을 실행하는 프로젝트의 경로. 머지 리퀘스트 파이프라인에서 이것은 소스 프로젝트의 경로입니다.
namespace_id
항상
잡을 실행하는 프로젝트의 네임스페이스 ID. 머지 리퀘스트 파이프라인에서 이것은 소스 프로젝트의 네임스페이스 ID입니다.
namespace_path
항상
잡을 실행하는 프로젝트의 네임스페이스 경로. 머지 리퀘스트 파이프라인에서 이것은 소스 프로젝트의 네임스페이스 경로입니다.
user_id
항상
잡을 실행하는 사용자의 ID.
user_login
항상
잡을 실행하는 사용자의 사용자 이름.
user_email
항상
잡을 실행하는 사용자의 이메일.
user_access_level
항상
잡을 실행하는 사용자의 액세스 수준. GitLab 16.9에서 도입.
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 목록(GitLab 16.0에서 도입).
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.
groups_direct
사용자가 0~200개 그룹의 직접 멤버인 경우
사용자의 직접 멤버십 그룹의 경로. 사용자가 200개 이상의 그룹의 직접 멤버인 경우 생략됩니다. (GitLab 16.11에서 도입되었으며 GitLab 17.3에서 ci_jwt_groups_direct 기능 플래그 뒤에 배치됨.
environment
잡이 환경을 지정하는 경우
이 잡이 배포하는 환경.
environment_protected
잡이 환경을 지정하는 경우
배포된 환경이 보호된 경우 true, 그렇지 않으면 false.
deployment_tier
잡이 환경을 지정하는 경우
잡이 지정하는 환경의 배포 티어. GitLab 15.2에서 도입.
environment_action
잡이 환경을 지정하는 경우
잡에 지정된 환경 동작(environment:action). (GitLab 16.5에서 도입)
runner_id
항상
잡을 실행하는 러너의 ID. GitLab 16.0에서 도입.
runner_environment
항상
잡에서 사용하는 러너의 유형. gitlab-hosted 또는 self-hosted 중 하나. GitLab 16.0에서 도입.
sha
항상
잡의 커밋 SHA. GitLab 16.0에서 도입.
ci_config_ref_uri
항상
최상위 파이프라인 정의의 참조 경로, 예를 들어 gitlab.example.com/my-group/my-project//.gitlab-ci.yml@refs/heads/main. GitLab 16.2에서 도입. 파이프라인 정의가 동일한 프로젝트에 있지 않으면 이 클레임은 null입니다.
ci_config_sha
항상
ci_config_ref_uri에 대한 Git 커밋 SHA. GitLab 16.2에서 도입. 파이프라인 정의가 동일한 프로젝트에 있지 않으면 이 클레임은 null입니다.
project_visibility
항상
파이프라인이 실행되는 프로젝트의 가시성. internal, private 또는 public일 수 있습니다. GitLab 16.3에서 도입.
job_source
항상
잡 소스. GitLab 18.9에서 도입.
job_config
정책에 의해 트리거된 잡
잡의 출처에 대한 메타데이터. 정책 잡의 경우 정책 구성에 대한 sha 및 url을 포함합니다. GitLab 18.9에서 도입.
{
"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": "sample-user@example.com",
"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분으로 설정됩니다.
문제 해결#
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 오류가 발생할 수 있습니다:
- ID 토큰 대신
$CI_JOB_JWT_V2와 같은 더 이상 사용되지 않는 토큰을 사용했습니다. 자세한 내용은 이전 버전의 JSON 웹 토큰이 더 이상 사용되지 않음을 참조하세요.
.gitlab-ci.yml 파일과 외부 서비스의 OIDC ID 공급자 구성 사이에서 provider_name 값이 일치하지 않습니다.
- GitLab에서 발급한 ID 토큰과 외부 서비스가 예상하는
aud(대상) 클레임이 누락되거나 일치하지 않습니다.
- GitLab CI/CD 잡에서
id_tokens: 블록을 활성화하거나 구성하지 않았습니다.
오류를 해결하려면 잡 내에서 토큰을 디코딩합니다:
echo $OIDC_TOKEN | cut -d '.' -f2 | base64 -d | jq .
다음을 확인합니다:
aud(대상)가 예상된 대상과 일치합니다(예: 외부 서비스의 URL).
sub(주제)가 서비스의 ID 공급자 설정에 매핑되어 있습니다.
preferred_username은 기본적으로 GitLab ID 토큰에 존재하지 않습니다.
