{{< details >}}

  • Tier: 旗舰版
  • Offering: JihuLab.com, 私有化部署

{{< /details >}}

极狐GitLab 密钥检测会在发现某些类型的泄露密钥时自动响应。自动响应可以:

  • 自动撤销密钥。
  • 通知发出密钥的合作伙伴。合作伙伴可以随后撤销密钥,通知其所有者,或采取其他措施防止滥用。

支持的密钥类型和操作

极狐GitLab 支持以下类型密钥的自动响应:

密钥类型 采取的操作 支持极狐GitLab.com 支持极狐GitLab 私有化部署
极狐GitLab 个人访问令牌 立即撤销令牌,发送邮件给所有者 ✅ 15.9 及以后
Amazon Web Services (AWS) IAM 访问密钥 通知 AWS
Google Cloud 服务账户密钥, API 密钥, 和 OAuth 客户端密钥 通知 Google Cloud
Postman API 密钥 通知 Postman;Postman 通知密钥所有者

组件图例

功能可用性

{{< history >}}

  • 在极狐GitLab 15.11 中,为非默认分支启用。

{{< /history >}}

凭证只有在密钥检测发现时才会进行后处理:

  • 在公共项目中,因为公开暴露的凭证构成了更大的威胁。
  • 在极狐GitLab 旗舰版项目中,出于技术原因。

高级架构

这个图描述了如何在极狐GitLab 应用中通过后处理钩子撤销密钥:

%%{init: { "fontFamily": "GitLab Sans" }}%% sequenceDiagram accTitle: Architecture diagram accDescr: How a post-processing hook revokes a secret in the GitLab application. autonumber GitLab Rails-->+GitLab Rails: gl-secret-detection-report.json GitLab Rails->>+GitLab Sidekiq: StoreScansService GitLab Sidekiq-->+GitLab Sidekiq: ScanSecurityReportSecretsWorker GitLab Sidekiq-->+GitLab Token Revocation API: GET revocable keys types GitLab Token Revocation API-->>-GitLab Sidekiq: OK GitLab Sidekiq->>+GitLab Token Revocation API: POST revoke revocable keys GitLab Token Revocation API-->>-GitLab Sidekiq: ACCEPTED GitLab Token Revocation API-->>+Partner API: revoke revocable keys Partner API-->>+GitLab Token Revocation API: ACCEPTED
  1. 一个包含密钥检测任务的流水线完成,产生扫描报告(1)。
  2. 报告由服务类处理(2),如果可能进行令牌撤销,则安排异步 worker。
  3. 异步 worker(3)与外部部署的 HTTP 服务通信(45),以确定哪些类型的密钥可以自动撤销。
  4. worker 发送(67)极狐GitLab 令牌撤销 API 能够撤销的检测到的密钥列表。
  5. 极狐GitLab 令牌撤销 API 将每个可撤销的令牌发送(89)到它们各自供应商的 合作伙伴 API。请参阅 极狐GitLab 令牌撤销 API 文档以获取更多信息。

泄露凭证通知的合作伙伴计划

极狐GitLab 在极狐GitLab.com 上的公共存储库中泄露的凭证时通知合作伙伴。

实现一个合作伙伴 API

合作伙伴 API 与极狐GitLab 令牌撤销 API 集成以接收和响应泄露令牌撤销请求。该服务应该是一个公开可访问的 HTTP API,支持幂等和速率限制。

对您的服务的请求可以包含一个或多个泄露的令牌,以及带有请求正文签名的头。我们强烈建议您使用此签名验证传入请求,以证明这是来自极狐GitLab 的真实请求。下面的图表详细描述了接收、验证和撤销泄露令牌的必要步骤:

%%{init: { "fontFamily": "GitLab Sans" }}%% sequenceDiagram accTitle: Partner API data flow accDescr: How a Partner API should receive and respond to leaked token revocation requests. autonumber GitLab Token Revocation API-->>+Partner API: Send new leaked credentials Partner API-->>+GitLab Public Keys endpoint: Get active public keys GitLab Public Keys endpoint-->>+Partner API: One or more public keys Partner API-->>+Partner API: Verify request is signed by GitLab Partner API-->>+Partner API: Respond to leaks Partner API-->>+GitLab Token Revocation API: HTTP status
  1. 极狐GitLab 令牌撤销 API 发送(1)一个撤销请求到合作伙伴 API。请求包含头信息,其中包含公共密钥标识符和请求正文签名。
  2. 合作伙伴 API 请求(2)来自极狐GitLab 的公共密钥列表。响应(3)可能包含多个公共密钥,以防密钥轮换,并应根据请求头中的标识符进行过滤。
  3. 合作伙伴 API 验证签名与实际请求正文相对照,使用公共密钥(4)。
  4. 合作伙伴 API 处理泄露的令牌,可能涉及自动撤销(5)。
  5. 合作伙伴 API 以适当的 HTTP 状态码响应极狐GitLab 令牌撤销 API(6):
    • 成功响应代码(HTTP 200 至 299)确认合作伙伴已接收并处理请求。
    • 错误代码(HTTP 400 或更高)导致极狐GitLab 令牌撤销 API 重试请求。

撤销请求

此 JSON 模式文档描述了撤销请求的主体:

{
    "type": "array",
    "items": {
        "description": "A leaked token",
        "type": "object",
        "properties": {
            "type": {
                "description": "The type of token. This is vendor-specific and can be customised to suit your revocation service",
                "type": "string",
                "examples": [
                    "my_api_token"
                ]
            },
            "token": {
                "description": "The substring that was matched by the Secret Detection analyser. In most cases, this is the entire token itself",
                "type": "string",
                "examples": [
                    "XXXXXXXXXXXXXXXX"
                ]
            },
            "url": {
                "description": "The URL to the raw source file hosted on GitLab where the leaked token was detected",
                "type": "string",
                "examples": [
                    "https://gitlab.example.com/some-repo/-/raw/abcdefghijklmnop/compromisedfile1.java"
                ]
            }
        }
    }
}

示例:

[{"type": "my_api_token", "token": "XXXXXXXXXXXXXXXX", "url": "https://example.com/some-repo/-/raw/abcdefghijklmnop/compromisedfile1.java"}]

在此示例中,密钥检测已确定 my_api_token 的一个实例已泄露。令牌的值提供给您,以及包含泄露令牌的文件的原始内容的公开可访问 URL。

请求包括两个特殊头:

类型 描述
Gitlab-Public-Key-Identifier string 用于签署此请求的密钥对的唯一标识符。主要用于帮助密钥轮换。
Gitlab-Public-Key-Signature string 请求正文的 base64 编码签名。

您可以使用这些头以及极狐GitLab 公共密钥端点来验证撤销请求是否真实。

公共密钥端点

极狐GitLab 维护一个公开可访问的端点,用于检索用于验证撤销请求的公共密钥。可以根据请求提供端点。

此 JSON 模式文档描述了公共密钥端点的响应主体:

{
    "type": "object",
    "properties": {
        "public_keys": {
            "description": "An array of public keys managed by GitLab used to sign token revocation requests.",
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "key_identifier": {
                        "description": "A unique identifier for the keypair. Match this against the value of the Gitlab-Public-Key-Identifier header",
                        "type": "string"
                    },
                    "key": {
                        "description": "The value of the public key",
                        "type": "string"
                    },
                    "is_current": {
                        "description": "Whether the key is currently active and signing new requests",
                        "type": "boolean"
                    }
                }
            }
        }
    }
}

示例:

{
    "public_keys": [
        {
            "key_identifier": "6917d7584f0fa65c8c33df5ab20f54dfb9a6e6ae",
            "key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEN05/VjsBwWTUGYMpijqC5pDtoLEf\nuWz2CVZAZd5zfa/NAlSFgWRDdNRpazTARndB2+dHDtcHIVfzyVPNr2aznw==\n-----END PUBLIC KEY-----\n",
            "is_current": true
        }
    ]
}

验证请求

您可以通过验证 Gitlab-Public-Key-Signature 头与请求正文的对照来检查撤销请求是否真实,使用从上面的 API 响应中获取的对应公共密钥。我们使用 ECDSA 与 SHA256 哈希来产生签名,然后将其 base64 编码到头值中。

下面的 Python 脚本演示了如何验证签名。它使用流行的 pyca/cryptography 模块进行加密操作:

import hashlib
import base64
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from cryptography.hazmat.primitives.asymmetric import ec

public_key = str.encode("")      # obtained from the public keys endpoint
signature_header = ""            # obtained from the `Gitlab-Public-Key-Signature` header
request_body = str.encode(r'')   # obtained from the revocation request body

pk = load_pem_public_key(public_key)
decoded_signature = base64.b64decode(signature_header)

pk.verify(decoded_signature, request_body, ec.ECDSA(hashes.SHA256()))  # throws if unsuccessful

print("Signature verified!")

主要步骤是:

  1. 将公共密钥加载到适合您使用的加密库的格式中。
  2. Gitlab-Public-Key-Signature 头值进行 base64 解码。
  3. 验证正文与解码签名的对照,指定 ECDSA 与 SHA256 哈希。