前言
Gitea 是一款轻量级的单体代码托管程序,而 Jenkins 则是业界历史悠久、插件生态丰富的经典持续集成(CI/CD)工具。在企业 DevOps 落地实践中,将两者结合是构建专属研发效能平台的常见选择。
然而,一个“完整”的集成方案通常包含两个核心维度:
- 代码与流水线集成:Jenkins 能够自动扫描 Gitea 仓库、拉取代码,并通过 Webhook 自动触发构建。
- 身份与权限集成: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 侧配置
- 安装插件:在 Jenkins 插件管理中搜索并安装 Gitea 插件。
- 添加凭证:进入 Manage Credentials,添加 Gitea Personal Access Token,填入上一步生成的 Token。
- 配置 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. 必须先澄清的三个概念
- 代码集成 ≠ 身份集成:Gitea 插件、Webhook、API Token 只解决“Jenkins 如何拉代码、如何被触发、用哪个服务账号调 API”的问题,不会让 Jenkins 识别 Gitea 的用户身份。
- Jenkins 的登录方式由 Security Realm(安全域)决定:默认使用本地用户数据库,因此 Gitea 用户再多,Jenkins 也“看不见”。
- 认证(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 应用
- 创建应用,Redirect URI 必须与 Jenkins 回调地址完全一致(含协议、域名、端口、路径):https://jenkins.example.com/securityRealm/finishLogin。
- 妥善保存 Client ID / Client Secret。
- 关注 Confidential(机密客户端) 选项:若应用被视为公共客户端,Gitea 会强制要求 PKCE。
- 服务端 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”,取消自动发现,手动配置如下:
| 配置项 | 推荐值 | 说明 |
| Issuer | https://git.example.com | 必须与 id_token 中 iss 一致 |
| Client ID / Secret | Gitea 应用凭证 | Secret 在 config.xml 中以加密串存储 |
| Authorization server url | /login/oauth/authorize | 登录跳转页 |
| Token server url | /login/oauth/access_token | 授权码换令牌 |
| Token Authentication Method | client_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 | 填普通登出页即可 |
| Scopes | openid 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(项目矩阵):
- 全局矩阵:管理员账号赋予 Overall/Administer(必须先给自己加权限,否则保存即被锁死);authenticated 组赋予基础 Read 权限;Anonymous 不授权。
- 项目级:进入 Job 配置,勾选 Enable project-based security,为指定 Gitea 用户名授予构建、读取等权限。
- 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><CLIENT_ID></clientId>
<clientSecret><加密后的_CLIENT_SECRET></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><强密码></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. 最终目标架构
- Jenkins Security Realm 切换为对接 Keycloak。
- Gitea 认证源接入 Keycloak(OAuth2/OIDC)。
- 关闭 Gitea 本地密码登录,强制跳转 Keycloak。
- 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 → 会话建立),每一步以日志堆栈为准,不凭印象推荐组件。