OpenID Connect (OIDC) 인증을 위한 ID 토큰 사용

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

GitLab CI/CD의
ID 토큰을 사용하여 타사 서비스와 인증할 수 있습니다.

ID 토큰

ID 토큰은 GitLab CI/CD 작업에 추가될 수 있는 JSON 웹 토큰(JWT)입니다.
이 토큰은 타사 서비스와의 OIDC 인증에 사용될 수 있으며, HashiCorp Vault와 인증하기 위해 secrets 키워드에 의해 사용됩니다.

ID 토큰은 .gitlab-ci.yml에서 구성됩니다. 예를 들면:

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 인스턴스의 도메인입니다(“발급자” 클레임).
sub project_path:{group}/{project}:ref_type:{type}:ref:{branch_name}(“주체” 클레임).
aud 토큰의 의도된 청중(“청중” 클레임). ID 토큰 구성에서 지정됩니다. 기본적으로 GitLab 인스턴스의 도메인입니다.
exp 만료 시간(“만료 시간” 클레임).
nbf 토큰이 유효해지는 시간(“사용 불가능 전” 클레임).
iat JWT가 발급된 시간(“발급 시간” 클레임).
jti 토큰의 고유 식별자(“JWT ID” 클레임).

토큰에는 GitLab에서 제공하는 사용자 정의 클레임도 포함됩니다:

필드 언제 설명
namespace_id 항상 그룹 또는 사용자 수준의 네임스페이스 범위를 ID로 사용할 때 사용됩니다.
namespace_path 항상 그룹 또는 사용자 수준의 네임스페이스 범위를 경로로 사용할 때 사용됩니다.
project_id 항상 프로젝트 범위를 ID로 사용할 때 사용됩니다.
project_path 항상 프로젝트 범위를 경로로 사용할 때 사용됩니다.
user_id 항상 작업을 실행하는 사용자의 ID입니다.
user_login 항상 작업을 실행하는 사용자의 사용자 이름입니다.
user_email 항상 작업을 실행하는 사용자의 이메일입니다.
user_access_level 항상 작업을 실행하는 사용자의 액세스 수준입니다. GitLab 16.9에서 도입됨.
user_identities 사용자 선호 설정 사용자의 외부 신원 목록입니다(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에서 도입됨.
{
  "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",
  "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 토큰 인증

ID 토큰을 사용하여 타사 서비스와 OIDC 인증을 수행할 수 있습니다. 예를 들어:

manual_authentication:
  variables:
    VAULT_ADDR: http://vault.example.com:8200
  image: vault:latest
  id_tokens:
    VAULT_ID_TOKEN:
      aud: http://vault.example.com
  script:
    - export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=myproject-example jwt=$VAULT_ID_TOKEN)"
    - export PASSWORD="$(vault kv get -field=password secret/myproject/example/db)"
    - my-authentication-script.sh $VAULT_TOKEN $PASSWORD

HashiCorp Vault와의 자동 ID 토큰 인증

Tier: Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

ID 토큰을 사용하여 HashiCorp Vault에서 비밀을 자동으로 가져올 수 있습니다.

secrets 키워드를 사용하세요.

이전에 CI_JOB_JWT를 사용하여 Vault에서 비밀을 가져온 경우, ID 토큰을 사용하도록 HashiCorp Vault 구성 업데이트 튜토리얼을 확인하여 전환하는 방법을 알아보세요.

자동 ID 토큰 인증 구성

ID 토큰이 하나 정의되면, secrets 키워드는 Vault와 인증하기 위해 이를 자동으로 사용합니다. 예를 들어:

job_with_secrets:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    PROD_DB_PASSWORD:
      vault: example/db/password # $VAULT_ID_TOKEN을 사용하여 인증
  script:
    - access-prod-db.sh --token $PROD_DB_PASSWORD

ID 토큰이 둘 이상 정의되면, token 키워드를 사용하여 어떤 토큰을 사용할지 지정합니다. 예를 들어:

job_with_secrets:
  id_tokens:
    FIRST_ID_TOKEN:
      aud: https://first.service.com
    SECOND_ID_TOKEN:
      aud: https://second.service.com
  secrets:
    FIRST_DB_PASSWORD:
      vault: first/db/password
      token: $FIRST_ID_TOKEN
    SECOND_DB_PASSWORD:
      vault: second/db/password
      token: $SECOND_ID_TOKEN
  script:
    - access-first-db.sh --token $FIRST_DB_PASSWORD
    - access-second-db.sh --token $SECOND_DB_PASSWORD

문제 해결

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