极狐GitLab 令牌故障排查

当使用极狐GitLab 令牌时,您可能会遇到以下问题。

过期的访问令牌

如果既有的访问令牌在使用且已达到 expires_at 值,则令牌会过期且:

  • 无法再用于身份验证。
  • 在 UI 中不可见。

使用此令牌的请求会返回 401 Unauthorized 响应。在短时间内从同一 IP 地址发出的太多未经授权的请求,会导致来自 JihuLab.com 的 403 Forbidden 响应。

对于认证请求限制的更多详情,可查阅 Git 和容器仓库失败身份验证禁用

从日志中识别过期的访问令牌

  • 引入于极狐GitLab 17.2。

先决条件:

您必须

要识别哪个 401 Unauthorized 请求失败是因为访问令牌过期导致,在 api_json.log 文件中使用以下字段:

字段名称 描述
meta.auth_fail_reason 请求被拒绝的原因。可能的值为: token_expiredtoken_revokedinsufficient_scopeimpersonation_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。您还可以使用 API 来 轮换令牌

替代过期访问令牌

要替代过期令牌:

  1. 检查此令牌之前可能已用于何处,然后从任何可能仍在使用令牌的自动化中删除它。
    • 对于个人访问令牌,使用 API 列出最近过期的令牌。例如,转到 https://jihulab.com/api/v4/personal_access_tokens,并定位具有特定 expires_at 日期的令牌。
    • 对于项目访问令牌,使用 项目访问令牌 API 列出最近过期的令牌。
    • 对于群组访问令牌,使用 群组访问令牌 API 列出最近过期的令牌。
  2. 创建新访问令牌:
  3. 用新访问令牌替代旧访问令牌。此过程取决于您如何使用令牌,例如如果配置为密钥或嵌入应用程序中。从此令牌发出的请求不应再返回 401 响应。

扩展令牌生命周期

使用此脚本延迟某些令牌的生命周期。

从极狐GitLab 16.0 开始,所有访问令牌都有一个过期日期。在部署至少极狐GitLab 16.0 版本后,任何未过期的访问令牌将在部署日期一年后过期。

如果这个日期临近,且还有令牌尚未轮换,您可以使用此脚本延迟过期时间,并给用户更多时间来轮换他们的令牌。

扩展特定令牌的生命周期

此脚本会扩展在指定日期过期的所有令牌的生命周期,包括:

  • 个人访问令牌
  • 群组访问令牌
  • 项目访问令牌

对于群组和项目访问令牌,此脚本仅在升级到极狐GitLab 16.0 或更高版本时自动为这些令牌分配过期日期时,才会延长这些令牌的生命周期。如果群组或项目访问令牌具有过期日期,或者已轮换,则该令牌的有效性取决于对资源的有效成员身份,因此使用此脚本无法延长该令牌的生命周期。

要使用脚本:

::Tabs

:::TabTitle Rails 控制台会话

  1. 在您的终端窗口,使用 sudo gitlab-rails console 启动 Rails 控制台回环。
  2. 请将整个 extend_expiring_tokens.rb 脚本粘贴到下面。如果想要,可以将 expiring_date 更改为其他日期。
  3. Enter

:::TabTitle 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 故障排查部分

::EndTabs

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 及更高版本时,任何个人项目访问令牌如果没有到期日期,自动在升级日期一年后设置到期日期。

在极狐GitLab 17.3 及以后,此自动设置过期日期的现有令牌已被撤销,您可以禁用对新访问令牌的到期日期强制执行

如果您不知道令牌何时过期,因为日期已经更改,那么在该日期尝试登录极狐GitLab 时,可能会出现意外的身份验证失败。

要管理此议题,您应该升级到极狐GitLab 17.2 或更高版本,因为这些版本包含一个可帮助您分析延长或删除令牌过期日期的工具

如果您无法运行工具,您可以在私有化部署实例上运行脚本来识别令牌:

  • 在特定日期过期。
  • 没有到期日期。

您可以采用如下方式在您的终端窗口上运行这些脚本:

您要运行的脚本取决于您是否已升级到极狐GitLab 16.0 或更高版本,或没有:

在您是被受此问题影响的令牌,您可以运行最终脚本来延长特定令牌的生命周期(如果需要)。

此脚本会以如下格式返回结果:

Expired group access token in Group ID 25, Token ID: 8, Name: Example Token, Scopes: ["read_api", "create_runner"], Last used:
Expired project access token in Project ID 2, Token ID: 9, Name: Test Token, Scopes: ["api", "read_registry", "write_registry"], Last used: 2022-02-11 13:22:14 UTC

找到所有特定日期过期的令牌

此脚本会找到在特定日期过期的令牌。

先决条件:

  • 您必须知道您的实例升级到极狐GitLab 16.0 的确切日期。

要使用它:

::Tabs

:::TabTitle Rails 控制台会话

  1. 在您的终端窗口,连接到您的实例。
  2. 使用 sudo gitlab-rails console 启动 Rails 控制台会话。
  3. 根据您的需要,复制整个 expired_tokens.rbexpired_tokens_date_range.rb 脚本,然后将其粘贴到控制台中。修改 expires_at_date 为您的实例升级到极狐GitLab 16.0 一年后的日期。
  4. Enter

:::TabTitle Rails Runner

  1. 在您的终端窗口,连接到您的实例。
  2. 根据您的需要,复制下面的 expired_tokens.rbexpired_tokens_date_range.rb 脚本,将其作为文件保存到实例上:
    • 将它命名为 expired_tokens.rb
    • expires_at_date 更改为您的实例升级到极狐GitLab 16.0 一年后的日期。
    • 文件必须可被 git:git 访问。
  3. 运行此命令,将路径更改为 expired_tokens.rb 文件的 完整 路径:

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

更多详情,请查阅 Rails Runner 故障排查部分

::EndTabs

expired_tokens.rb

此脚本需要您知道您的极狐GitLab 实例升级到极狐GitLab 16.0 的确切日期。

# Change this value to the date one year after your GitLab instance was upgraded.

expires_at_date = "2024-05-22"

# Check for expiring personal access tokens
PersonalAccessToken.owner_is_human.where(expires_at: expires_at_date).find_each do |token|
  if token.user.blocked?
    next
    # Hide unusable, blocked PATs from output
  end

  puts "Expired personal access token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end

# Check for expiring project and group access tokens
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) ? 'Group' : 'Project'

    puts "Expired #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
  end
end
note 对于不仅要隐藏,而且还要删除属于被阻止用户的令牌,请在 if token.user.blocked? 下直接添加 token.destroy!。然而,这种方法不会留下审计事件,这一点与 API 方法不同。

找到特定月份过期的令牌

此脚本会找到在特定月份过期的令牌。您不需要知道您的实例何时升级到 GitLab 16.0。要使用它:

::Tabs

:::TabTitle Rails 控制台会话

  1. 在您的终端窗口中,使用 sudo gitlab-rails console 启动 Rails 控制台会话。
  2. 将整个 tokens_with_no_expiry.rb 脚本粘贴到控制台中。如果想要,可以将 date_range 更改为不同的范围。
  3. 按下 Enter

:::TabTitle 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 故障排除部分

::EndTabs

expired_tokens_date_range.rb

# This script enables you to search for tokens that expire within a
# certain date range (like 1.month) from the current date. Use it if
# you're unsure when exactly your GitLab 16.0 upgrade completed.

date_range = 1.month

# Check for personal access tokens
PersonalAccessToken.owner_is_human.where(expires_at: Date.today .. Date.today + date_range).find_each do |token|
  puts "Expired personal access token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end

# Check for expiring project and group access tokens
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) ? 'Group' : 'Project'

    puts "Expired #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
  end
end

但大量令牌过期时识别过期日期

此脚本用于识别大量令牌过期的日期。您可以将其与本页上的其他脚本结合使用,以识别和扩展可能接近到期日期的大量令牌,以防您的团队尚未设置令牌轮换。

脚本以如下格式返回结果:

42 Personal access tokens will expire at 2024-06-27
17 Personal access tokens will expire at 2024-09-23
3 Personal access tokens will expire at 2024-08-13

要使用它:

::Tabs

:::TabTitle Rails 控制台会话

  1. 在您的终端窗口中,使用 sudo gitlab-rails console 启动 Rails 控制台会话。
  2. 将整个 dates_when_most_of_tokens_expire.rb 脚本粘贴到其中。
  3. Enter

:::TabTitle 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 故障排除部分

::EndTabs

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} Personal access tokens will expire at #{token.expires_at}"
  end

找到没有过期日期的令牌

此脚本会查找没有过期日期的令牌:expires_atNULL。对于尚未升级到极狐GitLab 16.0 或更高版本的用户,令牌 expires_at 的值为 NULL,可以用来识别需要添加过期日期的令牌。

您还可以在 Rails 控制台Rails Runner 中使用此脚本:

::Tabs

:::TabTitle Rails 控制台会话

  1. 在您的终端窗口中,连接到您的实例。
  2. 使用 sudo gitlab-rails console 启动 Rails 控制台会话。
  3. 粘贴整个 tokens_with_no_expiry.rb 脚本。
  4. Enter

:::TabTitle 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 故障排除部分

::EndTabs

tokens_with_no_expiry.rb

This script finds tokens without a value set for expires_at.

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

   # Check for expiring personal access tokens
   PersonalAccessToken.owner_is_human.where(expires_at: nil).find_each do |token|
     puts "Expires_at is nil for personal access token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
   end

   # Check for expiring project and group access tokens
   PersonalAccessToken.project_access_token.where(expires_at: nil).find_each do |token|
     token.user.members.each do |member|
       type = member.is_a?(GroupMember) ? 'Group' : 'Project'

       puts "Expires_at is nil for #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
     end
   end