Gitea 与 Jenkins 的集成实践

前言

Gitea 是一款轻量级的单体代码托管程序,而 Jenkins 则是业界历史悠久、插件生态丰富的经典持续集成(CI/CD)工具。在企业 DevOps 落地实践中,将两者结合是构建专属研发效能平台的常见选择。

然而,一个“完整”的集成方案通常包含两个核心维度:

  1. 代码与流水线集成:Jenkins 能够自动扫描 Gitea 仓库、拉取代码,并通过 Webhook 自动触发构建。
  2. 身份与权限集成:Gitea 中的开发者能够直接使用 Gitea 账号单点登录(SSO)Jenkins,并自动继承相应的权限。

本文结合一线运维实践,完整记录这两个维度的集成步骤,深度复盘在身份集成(OIDC/OAuth2)过程中遇到的各类报错与排错思路,并探讨向企业级统一身份认证(Keycloak)演进的架构路线。

安全提示:本文涉及的所有域名(如 git.example.com)、IP、Token 及密钥均已做脱敏处理,仅供技术参考。


一、 环境准备与基础配置

如果您尚未搭建基础环境,可以使用以下 docker-compose.yml 模板快速启动 Gitea 与 Jenkins 测试环境。

version: "3"
volumes:
  jenkins_home:
services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      – USER_UID=1000
      – USER_GID=1000
    restart: always
    volumes:
      – ./gitea_data:/data
      – /etc/timezone:/etc/timezone:ro
      – /etc/localtime:/etc/localtime:ro
    ports:
      – "3000:3000"

  jenkins:
    container_name: jenkins
    image: jenkins/jenkins:lts
    restart: on-failure
    privileged: true
    volumes:
      – jenkins_home:/var/jenkins_home
      – /var/run/docker.sock:/var/run/docker.sock
    ports:
      – "8080:8080"
      – "50000:50000"

Gitea Webhook 白名单配置

出于安全考虑,Gitea 默认限制了 Webhook 的目标地址。为了让 Gitea 能够成功将事件推送到 Jenkins,需要修改 Gitea 的 conf/app.ini,在 [webhook] 节点下配置白名单(生产环境建议填写 Jenkins 的具体 IP 或域名,而非 *):

[webhook]
ALLOWED_HOST_LIST = *

修改后需重启 Gitea 服务使其生效。


二、 维度一:代码与 CI/CD 集成 (Gitea Plugin)

Jenkins 本身不具备源码管理能力,通过安装官方的 Gitea Plugin,可以将 Jenkins 的 CI/CD 能力直接赋予 Gitea 上的组织或用户,实现“代码提交即触发构建”的无缝体验。

1. 创建 Gitea 服务账号与凭证

不建议使用个人账号进行系统集成。建议在 Gitea 中注册一个专用的服务账号(如 jenkins-svc),并将其加入到目标组织(如 GiteaTeam)的管理员组中。随后,为该账号生成一个 API Access Token,用于 Jenkins 调用 Gitea API 和拉取代码。

2. Jenkins 侧配置

  1. 安装插件:在 Jenkins 插件管理中搜索并安装 Gitea 插件。
  2. 添加凭证:进入 Manage Credentials,添加 Gitea Personal Access Token,填入上一步生成的 Token。
  3. 配置 Gitea Server:进入 Configure System,找到 Gitea Server 配置区:
  • Name: 自定义名称。
  • Server URL: 填写 Gitea 地址(如 https://git.example.com)。
  • Manage hooks: 务必勾选,并选择刚才配置的凭证。这将允许 Jenkins 自动在 Gitea 仓库中注入 Webhook。

3. 创建 Organization Folder 实现自动扫描

在 Jenkins 首页新建任务,选择 Organization Folder

  • 在 Repository Sources 中选择 Gitea Server,Owner 填写组织名(如 GiteaTeam),并选择对应的凭证。
  • 保存后,Jenkins 会自动扫描该组织下的所有仓库。只要仓库根目录包含 Jenkinsfile,Jenkins 就会自动将其纳入流水线队列,并自动在 Gitea 仓库中创建 Webhook。

4. 状态回显

当开发者在 Gitea 提交代码后,Jenkins 被触发构建。构建状态(如黄色的 ● 进行中,绿色的 ✔ 成功)会通过 API 回显到 Gitea 的提交列表中,开发者无需离开代码托管平台即可查看 CI 结果。


三、 维度二:身份与单点登录集成 (OIDC SSO)

痛点:代码流水线虽然打通,但 Jenkins 默认使用本地用户数据库。Gitea 中存在大量用户,若逐一在 Jenkins 中手动创建账号,维护成本极高。
目标:让 Gitea 用户直接使用 Gitea 账号登录 Jenkins,并按需分配权限。

1. 必须先澄清的三个概念

  1. 代码集成 ≠ 身份集成:Gitea 插件、Webhook、API Token 只解决“Jenkins 如何拉代码、如何被触发、用哪个服务账号调 API”的问题,不会让 Jenkins 识别 Gitea 的用户身份。
  2. Jenkins 的登录方式由 Security Realm(安全域)决定:默认使用本地用户数据库,因此 Gitea 用户再多,Jenkins 也“看不见”。
  3. 认证(Authentication)与授权(Authorization)是两回事:“能不能登录”是认证问题;“登录后能做什么”是授权问题,二者需分别配置。

2. 方案选型

方案说明适用场景
方案一:Jenkins 直连 Gitea OAuth2/OIDC登录时跳转 Gitea 授权,回调 Jenkins小规模、快速落地、Gitea 为唯一用户源
方案二:统一身份认证中心(Keycloak 等)Gitea 与 Jenkins 共同对接同一 IdP生产环境、多系统统一登录、长期运维
方案三:反向代理 SSO(oauth2-proxy 等)认证发生在网关层,Jenkins 信任代理头已有成熟网关体系

本次实践采用方案一落地,并以方案二作为架构演进目标。
已知局限:Gitea 的标准 OAuth2/OIDC 接口通常不返回组织/团队(Groups)信息,因此 Jenkins 侧难以按 Gitea 团队自动分配权限。若需“团队级权限自动映射”,应走方案二。

3. 前置知识与端点准备

Gitea 开启 OAuth2 Provider 不等于完整实现了 OpenID Connect (OIDC) 规范。许多版本下访问 /.well-known/openid-configuration 会返回 404,这是正常现象。因此 Jenkins 侧不能依赖“自动发现(Discovery)”,需要手动填写端点

以 https://git.example.com 为例,Gitea 端点速查表如下:

用途端点
授权端点/login/oauth/authorize
令牌端点/login/oauth/access_token
用户信息(OIDC 风格)/login/oauth/userinfo
用户信息(REST API 风格,备用)/api/v1/user
JWKS 公钥端点/login/oauth/keys(注意:不是 /login/oauth/certs)
登出页面/user/logout

4. 在 Gitea 创建 OAuth2 应用

  1. 创建应用,Redirect URI 必须与 Jenkins 回调地址完全一致(含协议、域名、端口、路径):https://jenkins.example.com/securityRealm/finishLogin。
  2. 妥善保存 Client ID / Client Secret
  3. 关注 Confidential(机密客户端) 选项:若应用被视为公共客户端,Gitea 会强制要求 PKCE。
  4. 服务端 app.ini 关注项:[server] ROOT_URL 需正确配置;[oauth2] ENABLE = true,且 JWT_SECRET 必须配置,否则 Gitea 无法签发 OIDC 所需的 id_token。

5. Jenkins OIDC 插件配置详解

安装 OpenID Connect Authentication Plugin (oic-auth),在 Security Realm 中选择 “Login with OpenID Connect”,取消自动发现,手动配置如下:

配置项推荐值说明
Issuerhttps://git.example.com必须与 id_token 中 iss 一致
Client ID / SecretGitea 应用凭证Secret 在 config.xml 中以加密串存储
Authorization server url/login/oauth/authorize登录跳转页
Token server url/login/oauth/access_token授权码换令牌
Token Authentication Methodclient_secret_basic若换令牌失败,切换为 client_secret_post
UserInfo server url/login/oauth/userinfo备用:/api/v1/user
Jwks server url/login/oauth/keys验签公钥;填错路径会导致 500 错误
End session URL/user/logout填普通登出页即可
Scopesopenid profile email必须包含 openid,否则插件内部校验崩溃

字段映射(Field Mapping):

  • User name field: preferred_username (使用 /api/v1/user 时改为 login)
  • Full name field: name 或 full_name
  • Email field: email
  • Groups field: 留空 (Gitea 不返回组)

6. 授权策略配置

登录打通后需解决授权。推荐使用 Project-based Matrix Authorization Strategy(项目矩阵)

  1. 全局矩阵:管理员账号赋予 Overall/Administer(必须先给自己加权限,否则保存即被锁死);authenticated 组赋予基础 Read 权限;Anonymous 不授权。
  2. 项目级:进入 Job 配置,勾选 Enable project-based security,为指定 Gitea 用户名授予构建、读取等权限。
  3. Folder 继承:将同类 Job 放入 Folder,在 Folder 上配置矩阵,子项目自动继承。

四、 排错实录:SSO 集成中的“八大坑位”

在 OIDC 对接过程中,由于协议差异和基础设施配置,极易出现各类异常。以下为真实踩坑记录:

坑位一:PowerShell 的 curl 别名

  • 现象:在 Windows PowerShell 中执行 curl -k 报参数错误。
  • 原因:PowerShell 的 curl 是 Invoke-WebRequest 的别名,不支持 -k 参数。
  • 对策:使用 curl.exe -k,或使用原生命令 Invoke-RestMethod -Uri <URL>。

坑位二:Discovery 端点 404

  • 现象:访问 /.well-known/openid-configuration 返回 404。
  • 结论:Gitea 未提供标准 OIDC 发现文档,属正常现象。
  • 对策:放弃自动发现,采用前文所述的手动端点配置。

坑位三:PKCE is required for public clients

  • 原因:Gitea 将应用判定为公共客户端,强制 PKCE,而 Jenkins 未携带。
  • 对策:在 Gitea 应用设置中勾选 Confidential(机密客户端);或在 Jenkins 插件中勾选 Use PKCE;或切换 Token Authentication Method。

坑位四:finishLogin 阶段 500

  • 原因:此阶段为“拿 code 换 token、验签、拉取用户信息”。常见原因包括:JWKS 路径错误(误填为 Keycloak 的 /certs);Scopes 不含 openid 导致无 id_token;Issuer 不匹配(尝试末尾加/去斜杠)。
  • 对策:核对端点路径,确保 Scopes 包含 openid,通过 System Log 检索异常堆栈。

坑位五:commenceLogin 阶段 500

  • 原因:此阶段为“构建跳转链接”,尚未发起外部请求。通常因为 Scopes 被改为纯 OAuth2 风格(如 read:user),OIDC 插件强制要求包含 openid,初始化即抛异常。
  • 对策:严格检查 Scopes 配置。

坑位六:反向代理 Nginx 的“静默失效”

  • 现象:Nginx 代理 Gitea 时,.well-known 被拦截,或安全规则失效。
  • 原因:若 Nginx 存在 location ~ /\. 拦截规则,会拦截 .well-known;若使用 location ^~ / 代理,由于 ^~ 优先级高于正则,会导致后方所有 location ~* … 的敏感文件拦截规则静默失效
  • 对策:增加 location ^~ /.well-known/ { allow all; } 优先放行;合理规划 Nginx 匹配优先级。

坑位七:Jenkins URL 缺失

  • 原因:OIDC 插件依赖全局 Jenkins URL 拼接回调地址。若读取不到 Root URL,会抛出空指针。
  • 对策:较新版本中该配置位于独立文件 jenkins.model.JenkinsLocationConfiguration.xml,需确保 <jenkinsUrl> 正确配置且末尾带斜杠。

坑位八:不存在的“通用 OAuth2 插件”

  • 反思:排错时曾试图寻找“Generic OAuth2 Plugin”,但官方市场并无此通用插件。
  • 教训:推荐插件前必须以实际检索为准。Gitea 场景下 oic-auth 仍是正确工具,核心在于端点与参数的精准调优。

五、 应急自救工具箱

1. 防锁定三件套

  • 修改前备份 config.xml(如 config.xml.bak)。
  • 保持一个已登录的管理员会话不关闭。
  • 一律使用浏览器无痕模式测试新认证链路。

2. config.xml 直改指南(脱敏示例)

若 UI 无法访问,可直接修改 Jenkins 根目录下的 config.xml:

<securityRealm class="org.jenkinsci.plugins.oic.OicSecurityRealm" plugin="oic-auth@x.x">
  <clientId>&lt;CLIENT_ID&gt;</clientId>
  <clientSecret>&lt;加密后的_CLIENT_SECRET&gt;</clientSecret>
  <userNameField>preferred_username</userNameField>
  <serverConfiguration class="org.jenkinsci.plugins.oic.OicServerManualConfiguration">
    <authorizationServerUrl>https://git.example.com/login/oauth/authorize</authorizationServerUrl>
    <tokenServerUrl>https://git.example.com/login/oauth/access_token</tokenServerUrl>
    <tokenAuthMethod>client_secret_basic</tokenAuthMethod>
    <jwksServerUrl>https://git.example.com/login/oauth/keys</jwksServerUrl>
    <endSessionUrl>https://git.example.com/user/logout</endSessionUrl>
    <scopes>openid profile email</scopes>
    <userInfoServerUrl>https://git.example.com/login/oauth/userinfo</userInfoServerUrl>
    <issuer>https://git.example.com</issuer>
  </serverConfiguration>
  <!– 逃生舱配置 –>
  <escapeHatchEnabled>true</escapeHatchEnabled>
  <escapeHatchUsername>admin</escapeHatchUsername>
  <escapeHatchSecret>&lt;强密码&gt;</escapeHatchSecret>
  <escapeHatchGroup>admin</escapeHatchGroup>
</securityRealm>

修改后必须重启 Jenkins 服务。

3. 逃生舱(Escape Hatch)

oic-auth 插件内置逃生舱:OIDC 链路崩溃时,可用硬编码的本地账号绕过外部认证直接以管理员身份登录,是打破“进不去 → 改不了”死循环的关键手段。

4. 彻底回退本地登录

若需完全回退,将整段 <securityRealm> 替换为:

<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">
  <disableSignup>true</disableSignup>
  <enableCaptcha>false</enableCaptcha>
</securityRealm>


六、 架构演进:引入 Keycloak 打造统一认证

1. 直连方案的局限与动机

直连方案解决了“能登录”的问题,但存在组信息缺失、权限维护成本高、多系统无法统一等痛点。引入 Keycloak 后,Gitea、Jenkins(及未来的 Harbor、SonarQube 等)共用一个 IdP,可实现统一登录与生命周期管理。

2. 关键事实与迁移路径

Gitea 不是 LDAP/AD 服务器,Keycloak 无法通过内置 User Federation 直接挂载 Gitea 数据库。用户迁移需走以下路径:

  • 路径一:API 脚本同步(推荐):编写脚本调用 Gitea Admin API 拉取用户,通过 Keycloak Admin API 批量创建。密码不迁移,通过 requiredActions: ["UPDATE_PASSWORD"] 强制首次登录改密。
  • 路径二:Partial Import 批量导入:将用户清单整理为 Keycloak 支持的 JSON 数组,在 Realm 的 Partial Import 中导入。
  • [
      { "username": "user_a", "email": "a@example.com",
        "enabled": true, "emailVerified": true,
        "requiredActions": ["UPDATE_PASSWORD"] }
    ]
  • 路径三:Identity Broker(过渡期):将 Gitea 配置为 Keycloak 的上游 IdP,用户首次登录时由 First Broker Login 流程自动在 Keycloak 建档。

3. 最终目标架构

  1. Jenkins Security Realm 切换为对接 Keycloak。
  2. Gitea 认证源接入 Keycloak(OAuth2/OIDC)。
  3. 关闭 Gitea 本地密码登录,强制跳转 Keycloak。
  4. Keycloak 组(Groups)映射到 Jenkins 角色,实现团队级权限自动化。

七、 收尾检查清单

  • Redirect URI 与 Jenkins URL 完全一致,且全程 HTTPS。
  • Scopes 包含 openid;JWKS 使用 /login/oauth/keys。
  • Gitea 应用为 Confidential 客户端,JWT_SECRET 已配置,ROOT_URL 正确。
  • 反向代理传递 Host 与 X-Forwarded-Proto,.well-known 已放行,并复核 ^~ 导致的正则规则失效问题。
  • 字段映射与 Gitea 实际返回 JSON 键名一致。
  • 授权策略已配置,且管理员账号具备 Administer 权限。
  • config.xml 已备份,逃生舱已验证可用。
  • 用户迁移与组权限规划已纳入 Keycloak 演进路线。

结语

Gitea 与 Jenkins 的深度集成,横跨了 代码流转(Webhook/API身份认证(OIDC/OAuth2 两个复杂的领域。

在身份集成的排错过程中,我们深刻体会到了协议细节(如 JWKS 路径、PKCE 机制)与基础设施(如 Nginx 代理优先级)对系统稳定性的影响。排错的核心方法论在于:先分清认证与授权,再逐阶段定位(commenceLogin → 授权页 → finishLogin → 会话建立),每一步以日志堆栈为准,不凭印象推荐组件。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注