OpenID Connect (OIDC)를 사용한 ID 토큰 인증
- GitLab 15.7에 도입되었습니다.
GitLab CI/CD의 ID 토큰을 사용하여 타사 서비스로 인증할 수 있습니다.
ID 토큰
ID 토큰은 GitLab CI/CD 작업에 추가할 수 있는 JSON Web Tokens (JWTs)입니다. 이 토큰은 타사 서비스와의 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
클레임을 가지고 있습니다. 타사 서비스는 바인딩된 오디언스와 일치하지 않는 토큰을 거부하도록 구성될 수 있습니다. 이 기능을 사용하여 토큰이 인증할 수 있는 서비스의 수를 줄이세요. 이렇게 하면 토큰이 노출되었을 때 심각도가 낮아집니다.
토큰 페이로드
다음의 표준 클레임이 각 ID 토큰에 포함됩니다:
필드 | 설명 |
---|---|
iss
| 토큰의 발급자, 기본적으로 GitLab 인스턴스의 도메인 (“issuer” 클레임). |
sub
|
project_path:{group}/{project}:ref_type:{type}:ref:{branch_name} (“subject” 클레임).
|
aud
| 토큰의 예상 오디언스 (“audience” 클레임). ID 토큰의 구성에 명시됩니다. 기본적으로 GitLab 인스턴스의 도메인입니다. |
exp
| 만료 시간 (“expiration time” 클레임). |
nbf
| 토큰이 유효해지는 시간 (“not before” 클레임). |
iat
| JWT가 발급된 시간 (“issued at” 클레임). |
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입니다. |
ref_type
| 항상 | Git ref 유형, branch 또는 tag .
|
ref_path
| 항상 | 작업의 완전히 정규화된 ref입니다. 예를 들어, refs/heads/main . GitLab 16.0에 도입되었습니다.
|
ref_protected
| 항상 | Git ref가 보호되어 있는지 여부. true 면 보호됨, 그렇지 않으면 false .
|
groups_direct
| 사용자가 직접 소속된 0에서 200개의 그룹 | 사용자가 직접 소속된 그룹의 경로입니다. 사용자가 200개 이상의 그룹에 속한 경우, 생략됩니다. (GitLab 16.11에 도입되었고 기능 플래그 ci_jwt_groups_direct 를 통해 GitLab 17.3에 포함되었습니다.)
|
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
| 항상 | 최상위 파이프라인 정의의 ref 경로입니다. 예를 들어, 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 인증을 위해 제3자 서비스에서 사용할 수 있습니다. 예를 들어:
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 토큰 인증
secrets
키워드를 사용하여 HashiCorp Vault에서 비밀을 자동으로 가져오려면 ID 토큰을 사용할 수 있습니다.
이전에 CI_JOB_JWT
를 사용하여 Vault에서 비밀을 가져왔다면, HashiCorp Vault 구성을 ID 토큰 사용으로 변경 튜토리얼에서 ID 토큰으로 전환하는 방법을 알아보세요.
자동 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