GitLab 토큰 문제 해결

GitLab 토큰을 사용할 때 다음과 같은 문제가 발생할 수 있습니다.

만료된 액세스 토큰

기존 액세스 토큰이 사용 중이고 expires_at 값을 도달하면 토큰이 만료되어 다음과 같은 문제가 발생합니다.

  • 더 이상 인증에 사용할 수 없음.
  • UI에서 표시되지 않음.

이 토큰을 사용하여 요청을 보내면 401 Unauthorized 응답이 반환됩니다. 동일한 IP 주소에서 짧은 시간 내에 너무 많은 권한이 없는 요청이 제출되면 GitLab.com에서는 403 Forbidden 응답이 반환됩니다.

인증 요청 제한에 대한 자세한 내용은 Git 및 컨테이너 레지스트리 실패 인증 금지을 참조하십시오.

로그에서 만료된 액세스 토큰 식별

전제 조건:

관리자이어야 합니다. api_json.log 파일에 액세스해야 합니다.

만료된 액세스 토큰으로 인해 실패한 401 Unauthorized 요청을 식별하려면 api_json.log 파일에서 다음 필드를 사용하십시오.

필드 이름 설명
meta.auth_fail_reason 요청이 거부된 이유. 가능한 값: token_expired, token_revoked, insufficient_scope, impersonation_disabled.
meta.auth_fail_token_id 시도된 토큰 유형 및 ID를 설명하는 문자열입니다.

사용자가 만료된 토큰을 사용하려고 하면 meta.auth_fail_reasontoken_expired입니다. 다음은 로그 항목에서 발췌한 내용입니다.

{
  "status": 401,
  "method": "GET",
  "path": "/api/v4/user",
  ...
  "meta.auth_fail_reason": "token_expired",
  "meta.auth_fail_token_id": "PersonalAccessToken/12",
}

meta.auth_fail_token_id는 ID가 12인 액세스 토큰을 사용했음을 나타냅니다.

이 토큰에 대한 자세한 정보는 개인 액세스 토큰 API를 사용하여 찾을 수 있습니다. 또한 토큰을 로테이트할 수도 있습니다.

만료된 액세스 토큰 교체

토큰을 교체하려면 다음 단계를 수행하십시오.

  1. 이전에 이 토큰을 사용했던 위치를 확인하고, 해당 토큰을 여전히 사용할 수 있는 자동화에서 제거하십시오.
    • 개인 액세스 토큰의 경우 최근에 만료된 토큰을 나열하려면 API를 사용하십시오. 예를 들어 https://gitlab.com/api/v4/personal_access_tokens로 이동하고 특정 expires_at 날짜를 가진 토큰을 찾으십시오.
    • 프로젝트 액세스 토큰의 경우 최근에 만료된 토큰을 나열하려면 프로젝트 액세스 토큰 API를 사용하십시오.
    • 그룹 액세스 토큰의 경우 최근에 만료된 토큰을 나열하려면 그룹 액세스 토큰 API를 사용하십시오.
  2. 새 액세스 토큰을 생성하십시오.
  3. 이전 액세스 토큰을 새 액세스 토큰으로 교체하십시오. 예를 들어 해당 토큰이 시크릿으로 구성되어 있는지 또는 응용 프로그램에 포함되어 있는지에 따라이 절차가 다를 수 있습니다. 이 토큰을 사용하여 보낸 요청은 더 이상 401 응답을 반환하지 않아야 합니다.

토큰 수명 연장

특정 토큰의 만료를 지연시킵니다.

GitLab 16.0 이후, 모든 액세스 토큰에는 만료 날짜가 있습니다. GitLab 16.0 이상을 최소 배포하면 만료되지 않는 액세스 토큰도 배포 날짜로부터 1년 후에 만료됩니다.

만일 이 날짜가 접근하고 있고 아직 로테이트되지 않은 토큰이 있다면, 이 스크립트를 사용하여 만료를 지연시키고 사용자가 토큰을 더 오래 로테이트할 수 있게 할 수 있습니다.

특정 토큰의 수명 연장

이 스크립트는 다음과 같이 지정된 날짜에 만료되는 모든 토큰의 기간을 연장합니다.

  • 개인 액세스 토큰
  • 그룹 액세스 토큰
  • 프로젝트 액세스 토큰

그룹 및 프로젝트 액세스 토큰의 경우,이 스크립트는 GitLab 16.0 이상으로 업그레이드될 때 자동으로 만료 날짜가 지정된 토큰의 수명 만 연장합니다. 그룹 또는 프로젝트 액세스 토큰이 만료 날짜로 생성되었거나 로테이트되었을 경우 이 스크립트로 유효성이 특정 자원의 유효한 멤버십에 따라 토큰 수명을 연장할 수 없습니다.

이 스크립트를 사용하려면:

Rails 콘솔 세션
  1. 터미널 창에서 sudo gitlab-rails console로 Rails 콘솔 세션을 시작하십시오.
  2. 아래 extend_expiring_tokens.rb 스크립트 전체를 붙여넣으십시오. 원하는 경우 expiring_date를 다른 날짜로 변경하십시오.
  3. Enter를 누르십시오.
Rails Runner
  1. 인스턴스에 연결하려면 터미널 창을 엽니다.
  2. 이전의 extend_expiring_tokens.rb 스크립트 전체를 복사하고 다음과 같이 인스턴스에 파일로 저장하십시오.
    • extend_expiring_tokens.rb라고 명명하십시오.
    • 원하는 경우 expiring_date를 다른 날짜로 변경하십시오.
    • 파일은 git:git에게 접근 가능해야 합니다.
  3. 다음 명령을 실행하십시오. /path/to/extend_expiring_tokens.rb를 자신의 extend_expiring_tokens.rb 파일의 전체 경로로 변경하십시오.

    sudo gitlab-rails runner /path/to/extend_expiring_tokens.rb
    

자세한 정보는 Rails Runner troubleshooting section을 참조하십시오.

extend_expiring_tokens.rb
expiring_date = Date.new(2024, 5, 30)
new_expires_at = 6.months.from_now

total_updated = PersonalAccessToken
                  .not_revoked
                  .without_impersonation
                  .where(expires_at: expiring_date.to_date)
                  .update_all(expires_at: new_expires_at.to_date)

puts "Updated #{total_updated} tokens with new expiry date #{new_expires_at}"

특정 날짜에 만료되는 개인, 프로젝트 및 그룹 액세스 토큰 식별

만료 날짜가 없는 엑세스 토큰은 무기한 유효하며 토큰이 노출되면 보안 위험이 발생합니다.

이 위험을 관리하기 위해 GitLab 16.0 이상으로 업그레이드하면 만료 날짜가 없는 개인, 프로젝트, 또는 그룹 액세스 토큰에 자동으로 업그레이드 날짜가 설정됩니다. 업그레이드된 토큰의 만료일은 업그레이드 날짜로부터 1년 후로 설정됩니다.

GitLab 17.3 이후로, 기존 토큰의 만료일 설정이 자동으로 되돌려졌으며, 새로운 액세스 토큰에 대한 만료일 강제 설정을 해제할 수 있습니다.

날짜가 변경되어 토큰 만료일을 알 수 없는 경우, 해당 날짜에 GitLab에 로그인하려고 할 때 예기치 않은 인증 실패가 발생할 수 있습니다.

이 문제를 관리하기 위해 GitLab 17.2 이상으로 업그레이드해야 합니다. 이 버전에는 토큰 만료일을 분석, 연장 또는 제거하는 데 도움이 되는 도구가 포함되어 있습니다.

이 도구를 실행할 수 없는 경우, 자체 관리형 인스턴스에서 다음을 식별하는 스크립트를 실행할 수도 있습니다.

  • 특정 날짜에 만료되는 토큰.
  • 만료 날짜가 없는 토큰.

이러한 스크립트는 다음 위치의 터미널 창에서 실행합니다.

실행할 특정 스크립트는 GitLab 16.0 이상으로 업그레이드했는지 여부에 따라 다릅니다.

이 문제를 사용하여 파악한 후에는 필요한 경우 특정 토큰의 수명을 연장하는 마지막 스크립트를 실행할 수 있습니다.

이러한 스크립트는 다음 형식으로 결과를 반환합니다.

Group ID 25의 만료된 그룹 엑세스 토큰, 토큰 ID: 8, 이름: Example Token, 범위: ["read_api", "create_runner"], 최종 사용 시간:
Project ID 2의 만료된 프로젝트 엑세스 토큰, 토큰 ID: 9, 이름: Test Token, 범위: ["api", "read_registry", "write_registry"], 최종 사용 시간: 2022-02-11 13:22:14 UTC

자세한 정보는 이슈 18003를 참조하세요.

특정 날짜에 만료되는 모든 토큰 찾기

이 스크립트는 특정 날짜에 만료되는 토큰을 찾습니다.

필수 조건:

  • 인스턴스가 GitLab 16.0으로 업그레이드된 정확한 날짜를 알아야 합니다.

사용 방법:

Rails 콘솔 세션
  1. 터미널 창에서 인스턴스에 연결합니다.
  2. sudo gitlab-rails console로 Rails 콘솔 세션을 시작합니다.
  3. 필요에 따라 expired_tokens.rb 또는 expired_tokens_date_range.rb 전체 스크립트를 복사하고 콘솔에 붙여넣습니다. expires_at_date를 GitLab 16.0으로 업그레이드된 인스턴스의 1년 후 날짜로 변경합니다.
  4. Enter를 누릅니다.
Rails Runner
  1. 터미널 창에서 인스턴스에 연결합니다.
  2. 필요에 따라 expired_tokens.rb 또는 expired_tokens_date_range.rb 전체 스크립트를 복사하고 다음과 같이 인스턴스에 파일로 저장합니다.
    • expired_tokens.rb로 이름을 지정합니다.
    • expires_at_date를 GitLab 16.0으로 업그레이드된 인스턴스의 1년 후 날짜로 변경합니다.
    • 파일은 git:git에게 접근 가능해야 합니다.
  3. 다음 명령을 실행합니다. 경로를 전체 경로로 변경하세요.

    sudo gitlab-rails runner /path/to/expired_tokens.rb
    

자세한 내용은 Rails Runner troubleshooting section을 참조하세요.

expired_tokens.rb

이 스크립트를 사용하려면 GitLab 인스턴스가 GitLab 16.0으로 업그레이드된 정확한 날짜를 알아야 합니다.

# 이 값을 GitLab 인스턴스가 업그레이드된 1년 후의 날짜로 변경합니다.

expires_at_date = "2024-05-22"

# 만료된 개인 액세스 토큰 확인
PersonalAccessToken.owner_is_human.where(expires_at: expires_at_date).find_each do |token|
  if token.user.blocked?
    next
    # 사용 불가, 차단된 PAT는 결과에서 숨김
  end

  puts "만료된 개인 액세스 토큰 ID: #{token.id}, 사용자 이메일: #{token.user.email}, 이름: #{token.name}, 범위: #{token.scopes}, 최종 사용 시간: #{token.last_used_at}"
end

# 만료된 프로젝트 및 그룹 액세스 토큰 확인
PersonalAccessToken.project_access_token.where(expires_at: expires_at_date).find_each do |token|
  token.user.members.each do |member|
    type = member.is_a?(GroupMember) ? '그룹' : '프로젝트'

    puts "#{type} ID #{member.source_id}의 만료된 #{type} 액세스 토큰, 토큰 ID: #{token.id}, 이름: #{token.name}, 범위: #{token.scopes}, 최종 사용 시간: #{token.last_used_at}"
  end
end

참고: 차단된 사용자에게 속한 토큰을 숨기는 것뿐만 아니라 제거하기 위해 if token.user.blocked? 바로 아래에 token.destroy!를 추가하세요. 이 작업은 API 메서드와 달리 감사 이벤트를 남기지 않습니다.

지정된 달에 만료되는 토큰 찾기

이 스크립트는 특정 달에 만료되는 토큰을 찾습니다. GitLab 16.0으로 업그레이드된 정확한 날짜를 알 필요가 없습니다. 사용 방법은 다음과 같습니다:

Rails 콘솔 세션
  1. 터미널 창에서 sudo gitlab-rails console 명령으로 Rails 콘솔 세션을 시작합니다.
  2. 아래의 tokens_with_no_expiry.rb 스크립트 전체를 붙여넣습니다. 원하는 경우 date_range를 다른 범위로 변경합니다.
  3. Enter를 누릅니다.
Rails Runner
  1. 터미널 창에서 인스턴스에 연결합니다.
  2. tokens_with_no_expiry.rb 스크립트 전체를 복사하여 인스턴스에 파일로 저장합니다.
    • 파일 이름: expired_tokens_date_range.rb.
    • 원하는 경우 date_range를 다른 범위로 변경합니다.
    • 파일은 git:git에서 액세스할 수 있어야 합니다.
  3. 다음 명령을 실행합니다. /path/to/expired_tokens_date_range.rb 를 실제 expired_tokens_date_range.rb 파일의 전체 경로로 변경합니다:

    sudo gitlab-rails runner /path/to/expired_tokens_date_range.rb
    

자세한 정보는 Rails Runner 문제 해결 섹션을 참조하세요.

expired_tokens_date_range.rb

# 이 스크립트를 사용하면 특정 날짜 범위(예: 1개월) 내에 만료되는 토큰을 검색할 수 있습니다.
# GitLab 16.0 업그레이드가 정확히 언제 완료되었는지 모르는 경우 사용합니다.

date_range = 1.month

# 개인 액세스 토큰 확인
PersonalAccessToken.owner_is_human.where(expires_at: Date.today .. Date.today + date_range).find_each do |token|
  puts "만료된 개인 액세스 토큰 ID: #{token.id}, 사용자 이메일: #{token.user.email}, 이름: #{token.name}, 스코프: #{token.scopes}, 마지막 사용: #{token.last_used_at}"
end

# 만료되는 프로젝트 및 그룹 액세스 토큰 확인
PersonalAccessToken.project_access_token.where(expires_at: Date.today .. Date.today + date_range).find_each do |token|
  token.user.members.each do |member|
    type = member.is_a?(GroupMember) ? '그룹' : '프로젝트'

    puts "#{type} ID #{member.source_id}의 만료된 #{type} 액세스 토큰, 토큰 ID: #{token.id}, 이름: #{token.name}, 스코프: #{token.scopes}, 마지막 사용: #{token.last_used_at}"
  end
end

많은 토큰이 만료되는 날짜 식별하기

이 스크립트는 대부분의 토큰이 만료되는 날짜를 식별합니다. 이 스크립트를 이 페이지의 다른 스크립트와 결합하여 사용하여, 팀이 아직 토큰 회전을 설정하지 않은 대규모 토큰의 만료일을 식별하고 연장할 수 있습니다.

이 스크립트는 다음 형식으로 결과를 반환합니다:

42개의 개인 액세스 토큰이 2024-06-27에 만료됩니다
17개의 개인 액세스 토큰이 2024-09-23에 만료됩니다
3개의 개인 액세스 토큰이 2024-08-13에 만료됩니다

사용 방법은 다음과 같습니다:

Rails 콘솔 세션
  1. 터미널 창에서 sudo gitlab-rails console 명령으로 Rails 콘솔 세션을 시작합니다.
  2. dates_when_most_of_tokens_expire.rb 스크립트 전체를 붙여넣습니다.
  3. Enter를 누릅니다.
Rails Runner
  1. 터미널 창에서 인스턴스에 연결합니다.
  2. dates_when_most_of_tokens_expire.rb 스크립트 전체를 복사하여 인스턴스에 파일로 저장합니다.
    • 파일 이름: dates_when_most_of_tokens_expire.rb.
    • 파일은 git:git에서 액세스할 수 있어야 합니다.
  3. 다음 명령을 실행합니다. /path/to/dates_when_most_of_tokens_expire.rb 를 실제 dates_when_most_of_tokens_expire.rb 파일의 전체 경로로 변경합니다:

    sudo gitlab-rails runner /path/to/dates_when_most_of_tokens_expire.rb
    

자세한 정보는 Rails Runner 문제 해결 섹션을 참조하세요.

dates_when_most_of_tokens_expire.rb

PersonalAccessToken
  .select(:expires_at, Arel.sql('count(*)'))
  .where('expires_at >= NOW()')
  .group(:expires_at)
  .order(Arel.sql('count(*) DESC'))
  .limit(10)
  .each do |token|
    puts "#{token.count}개의 개인 액세스 토큰이 #{token.expires_at}에 만료됩니다"
  end

만료 날짜가 정해지지 않은 토큰 찾기

이 스크립트는 만료 날짜가 지정되지 않은 토큰을 찾습니다: expires_atNULL인 경우입니다. 아직 GitLab 버전 16.0 이상으로 업그레이드하지 않은 사용자에게는 토큰 expires_at 값이 NULL이며, 만료 날짜를 추가해야 하는 토큰을 식별하는 데 사용할 수 있습니다.

Rails 콘솔 또는 Rails Runner 중 하나에서 이 스크립트를 사용할 수 있습니다:

Rails 콘솔 세션
  1. 터미널 창에서 인스턴스에 연결합니다.
  2. sudo gitlab-rails console 명령으로 Rails 콘솔 세션을 시작합니다.
  3. tokens_with_no_expiry.rb 스크립트 전체를 붙여넣습니다.
  4. Enter를 누릅니다.
Rails Runner
  1. 터미널 창에서 인스턴스에 연결합니다.
  2. tokens_with_no_expiry.rb 스크립트 전체를 복사하여 인스턴스에 파일로 저장합니다.
    • 파일 이름: tokens_with_no_expiry.rb.
    • 파일은 git:git에서 액세스할 수 있어야 합니다.
  3. 다음 명령을 실행합니다. tokens_with_no_expiry.rb 파일의 전체 경로로 변경합니다:

    sudo gitlab-rails runner /path/to/tokens_with_no_expiry.rb
    

자세한 정보는 Rails Runner 문제 해결 섹션을 참조하세요.

tokens_with_no_expiry.rb

expires_at 값이 설정되지 않은 토큰을 찾습니다.

# This script finds tokens which do not have an expires_at value set.

# 만료되는 개인 액세스 토큰 확인
PersonalAccessToken.owner_is_human.where(expires_at: nil).find_each do |token|
  puts "만료 날짜가 설정되지 않은 개인 액세스 토큰 ID: #{token.id}, 사용자 이메일: #{token.user.email}, 이름: #{token.name}, 스코프: #{token.scopes}, 마지막 사용: #{token.last_used_at}"
end

# 만료되는 프로젝트 및 그룹 액세스 토큰 확인
PersonalAccessToken.project_access_token.where(expires_at: nil).find_each do |token|
  token.user.members.each do |member|
    type = member.is_a?(GroupMember) ? '그룹' : '프로젝트'

    puts "#{type} ID #{member.source_id}에서 만료되는 #{type} 액세스 토큰, 토큰 ID: #{token.id}, 이름: #{token.name}, 스코프: #{token.scopes}, 마지막 사용: #{token.last_used_at}"
  end
end