Git 服务器搭建完全指南 / 第 11 章 - 认证集成
第 11 章 - 认证集成
企业环境中,Git 服务器需要与现有的身份认证系统集成,实现统一登录(SSO)和集中用户管理。
11.1 认证方案概览
| 方案 | 适用场景 | 复杂度 | Gitea | GitLab |
|---|---|---|---|---|
| LDAP/AD | 企业内部有 AD/LDAP | 中 | ✅ | ✅ |
| OAuth2 | 使用第三方 IdP | 中 | ✅ | ✅ |
| SAML | 企业 SSO(如 Okta) | 高 | ✅ | ✅ (EE) |
| OpenID Connect | 现代 Web SSO | 中 | ✅ | ✅ |
| CAS | 高校/传统企业 | 中 | ✅ | ✅ |
| SMTP | 邮件账号登录 | 低 | ✅ | ✅ |
11.2 LDAP 集成
11.2.1 LDAP 基础概念
LDAP 目录树结构:
dc=example,dc=com (根)
├── ou=people (用户)
│ ├── uid=alice
│ ├── uid=bob
│ └── uid=charlie
├── ou=groups (组)
│ ├── cn=developers
│ ├── cn=admins
│ └── cn=ops
└── ou=service (服务账号)
└── cn=git-bind
11.2.2 Gitea LDAP 配置
Web 界面配置:
- 登录管理后台 →
Administration→Authentication - 点击
Add Authentication Source - 选择
LDAP (via BindDN)或LDAP (simple auth)
API 配置:
# 添加 LDAP 认证源
curl -s -X POST -H "Authorization: token $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
"$GITEA_URL/api/v1/admin/auths" \
-d '{
"type": "LDAP",
"name": "公司 LDAP",
"host": "ldap.example.com",
"port": 389,
"security_protocol": "StartTLS",
"bind_dn": "cn=git-bind,ou=service,dc=example,dc=com",
"bind_password": "ldap_bind_password",
"user_search_base": "ou=people,dc=example,dc=com",
"user_filter": "(&(objectClass=posixAccount)(uid=%s))",
"admin_filter": "(memberOf=cn=admins,ou=groups,dc=example,dc=com",
"username_attribute": "uid",
"first_name_attribute": "givenName",
"surname_attribute": "sn",
"email_attribute": "mail",
"public_ssh_key_attribute": "sshPublicKey",
"groups_attribute": "memberOf",
"group_filter": "",
"group_dn": "",
"group_membership_uid": "",
"skip_local_2fa": false,
"is_sync_enabled": true,
"is_active": true
}'
11.2.3 LDAP 配置参数说明
| 参数 | 说明 | 示例 |
|---|---|---|
| Host | LDAP 服务器地址 | ldap.example.com |
| Port | LDAP 端口 | 389 (LDAP), 636 (LDAPS) |
| Bind DN | 服务账号 DN | cn=git-bind,ou=service,dc=example,dc=com |
| Bind Password | 服务账号密码 | (安全存储) |
| User Search Base | 用户搜索基线 | ou=people,dc=example,dc=com |
| User Filter | 用户过滤器 | (&(objectClass=posixAccount)(uid=%s)) |
| Admin Filter | 管理员过滤器 | (memberOf=cn=admins,…) |
| Username Attribute | 用户名属性 | uid / sAMAccountName |
| Email Attribute | 邮箱属性 |
11.2.4 常用 LDAP 过滤器
# Active Directory 用户
(&(objectCategory=Person)(sAMAccountName=%s)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))
# OpenLDAP 用户
(&(objectClass=posixAccount)(uid=%s))
# 仅活跃用户
(&(objectClass=person)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(uid=%s))
# 管理员组成员
(memberOf=cn=git-admins,ou=groups,dc=example,dc=com)
# 多组成员(OR)
(|(memberOf=cn=devs,...)(memberOf=cn=ops,...))
11.2.5 GitLab LDAP 配置
# /etc/gitlab/gitlab.rb
gitlab_rails['ldap_enabled'] = true
gitlab_rails['ldap_servers'] = {
'main' => {
'label' => '公司 LDAP',
'host' => 'ldap.example.com',
'port' => 389,
'uid' => 'sAMAccountName',
'encryption' => 'start_tls',
'bind_dn' => 'CN=git-bind,OU=Service,DC=example,DC=com',
'password' => 'ldap_bind_password',
'active_directory' => true,
'base' => 'OU=People,DC=example,DC=com',
'verify_certificates' => true,
'allow_username_or_email_login' => true,
'block_auto_created_users' => false,
'group_base' => 'OU=Groups,DC=example,DC=com',
'admin_group' => 'git-admins',
'sync_ssh_keys' => 'sshPublicKey',
# 属性映射
'attributes' => {
'username' => ['sAMAccountName', 'uid'],
'email' => ['mail', 'email'],
'name' => 'displayName',
'first_name' => 'givenName',
'last_name' => 'sn'
}
}
}
# 应用配置
sudo gitlab-ctl reconfigure
# 测试 LDAP 连接
sudo gitlab-rake gitlab:ldap:check
# 同步 LDAP 用户
sudo gitlab-rake gitlab:ldap:sync
11.2.6 LDAP 用户同步
#!/bin/bash
# ldap-sync.sh - 定期同步 LDAP 用户
GITEA_URL="https://git.example.com"
ADMIN_TOKEN="your_admin_token"
# 触发 LDAP 同步
curl -s -X POST -H "Authorization: token $ADMIN_TOKEN" \
"$GITEA_URL/api/v1/admin/user/repo_migrate" || true
# 使用 ldapsearch 验证连接
ldapsearch -x -H ldap://ldap.example.com \
-D "cn=git-bind,ou=service,dc=example,dc=com" \
-w "bind_password" \
-b "ou=people,dc=example,dc=com" \
"(objectClass=posixAccount)" uid mail
echo "LDAP 同步完成: $(date)"
11.2.7 LDAP 调试
# 测试 LDAP 连接
ldapsearch -x -H ldap://ldap.example.com \
-D "cn=git-bind,ou=service,dc=example,dc=com" \
-w "bind_password" \
-b "ou=people,dc=example,dc=com" \
"(&(objectClass=posixAccount)(uid=alice))"
# 使用 LDAPS
ldapsearch -x -H ldaps://ldap.example.com:636 \
-D "cn=git-bind,ou=service,dc=example,dc=com" \
-w "bind_password" \
-b "ou=people,dc=example,dc=com" \
-ZZ \
"(uid=alice)"
# 测试 Gitea LDAP 认证
curl -s -X POST "$GITEA_URL/api/v1/users/alianame/tokens" \
-u "alianame:ldap_password"
11.3 OAuth2 集成
11.3.1 OAuth2 流程
用户浏览器 Gitea/Forgejo OAuth2 Provider
│ │ │
│── 点击登录 ──────►│ │
│ │── 重定向 ─────────────►│
│◄─── 登录页面 ──────│◄──────────────────────│
│ │ │
│── 输入凭据 ──────────────────────────────►│
│◄── 授权码 ───────────────────────────────│
│ │ │
│── 回调带授权码 ──►│ │
│ │── 用授权码换 Token ───►│
│ │◄── Access Token ─────│
│ │ │
│ │── 获取用户信息 ───────►│
│ │◄── 用户资料 ─────────│
│ │ │
│◄── 登录成功 ──────│ │
11.3.2 Gitea OAuth2 配置
支持的 OAuth2 Provider:
| Provider | 类型 | 说明 |
|---|---|---|
| GitHub | OAuth2 | 使用 GitHub 账号登录 |
| GitLab | OAuth2 | 使用 GitLab.com 账号登录 |
| OAuth2 | Google Workspace | |
| Keycloak | OpenID Connect | 自托管 IdP |
| 自定义 | OAuth2/OIDC | 任意 OAuth2 兼容服务 |
配置 Keycloak OAuth2:
# 添加 OAuth2 认证源
curl -s -X POST -H "Authorization: token $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
"$GITEA_URL/api/v1/admin/auths" \
-d '{
"type": "OAuth2",
"name": "Keycloak",
"provider": "openidConnect",
"client_id": "gitea-client",
"client_secret": "your-client-secret",
"auto_discover_url": "https://keycloak.example.com/realms/master/.well-known/openid-configuration",
"icon_url": "https://keycloak.example.com/icon.png",
"required_claim_name": "",
"required_claim_value": "",
"group_claim_name": "groups",
"admin_group": "gitea-admins",
"skip_local_2fa": true,
"is_auto_discover": true,
"allow_deactivate_2fa": false,
"is_sync_enabled": true,
"is_active": true
}'
11.3.3 GitLab OAuth2 配置
# /etc/gitlab/gitlab.rb
# Keycloak (OpenID Connect)
gitlab_rails['omniauth_enabled'] = true
gitlab_rails['omniauth_allow_single_sign_on'] = ['openid_connect']
gitlab_rails['omniauth_block_auto_created_users'] = false
gitlab_rails['omniauth_providers'] = [
{
name: 'openid_connect',
label: 'Keycloak SSO',
args: {
name: 'openid_connect',
scope: ['openid', 'profile', 'email'],
response_type: 'code',
issuer: 'https://keycloak.example.com/realms/master',
discovery: true,
client_auth_method: 'query',
uid_field: 'preferred_username',
send_scope_to_token_endpoint: 'true',
client_options: {
identifier: 'gitlab-client',
secret: 'your-client-secret',
redirect_uri: 'https://git.example.com/users/auth/openid_connect/callback'
}
}
}
]
# GitHub OAuth
gitlab_rails['omniauth_providers'] = [
{
name: 'github',
label: 'GitHub',
app_id: 'your-github-client-id',
app_secret: 'your-github-client-secret',
args: { scope: 'user:email' }
}
]
sudo gitlab-ctl reconfigure
11.4 SAML 集成
11.4.1 SAML 工作原理
用户 Gitea/GitLab IdP (如 Okta, ADFS)
│ │ │
│── 访问 Git ──────►│ │
│ │── SAMLRequest ─────────►│
│◄── 重定向到 IdP ──│◄──────────────────────│
│ │ │
│── 登录 IdP ──────────────────────────────►│
│◄── SAMLResponse ─────────────────────────│
│ │ │
│── 提交 Response ─►│ │
│ │── 验证签名和属性 ──────│
│ │ │
│◄── 登录成功 ──────│ │
11.4.2 Gitea SAML 配置
Gitea 原生支持 SAML(需要通过 OAuth2/OIDC 桥接或直接配置)。
通常推荐的方案是通过 Keycloak 同时支持 SAML 和 OIDC:
- 在 Keycloak 中创建 SAML Client
- Gitea 通过 OpenID Connect 连接到 Keycloak
- Keycloak 在后端通过 SAML 与企业 IdP 联合认证
11.5 Keycloak 统一身份管理
11.5.1 Keycloak 安装
# docker-compose.yml
version: '3'
services:
keycloak:
image: quay.io/keycloak/keycloak:24.0
container_name: keycloak
restart: always
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin_password
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://keycloak-db:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak_password
KC_HOSTNAME: keycloak.example.com
KC_HTTP_RELATIVE_PATH: /auth
ports:
- "8080:8080"
- "8443:8443"
command: start-dev
depends_on:
- keycloak-db
keycloak-db:
image: postgres:16-alpine
container_name: keycloak-db
restart: always
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: keycloak_password
volumes:
- keycloak-data:/var/lib/postgresql/data
volumes:
keycloak-data:
11.5.2 配置 Gitea 客户端
在 Keycloak 管理界面:
- 创建 Realm(如
git) - 创建 Client:
- Client ID:
gitea - Client Protocol:
openid-connect - Root URL:
https://git.example.com - Valid Redirect URIs:
https://git.example.com/user/oauth2/keycloak/callback
- Client ID:
- 创建 Client Secret
- 配置 Mappers:
preferred_username→ 用户名email→ 邮箱groups→ 组(用于 GitLab group sync)
11.6 SSH 密钥管理
11.6.1 LDAP 同步 SSH 密钥
如果 LDAP 中存储了 SSH 公钥(如 sshPublicKey 属性),Gitea 可以自动同步:
# 确认 LDAP 配置中设置了 sshPublicKey
# Gitea → Administration → Authentication → LDAP
# SSH Public Key Attribute: sshPublicKey
# 手动触发同步
# Gitea → Site Administration → User Accounts → 同步用户
11.6.2 SSH 证书认证
企业级环境推荐使用 SSH 证书代替个人密钥:
# 在 CA 服务器上签发用户证书
ssh-keygen -s ca_key -I "alice@example.com" \
-n alice \
-V +52w \
alice_key.pub
# 生成 alice_key-cert.pub
# 配置 SSH 服务器信任 CA
echo "@cert-authority *.example.com $(cat ca_key.pub)" >> /etc/ssh/ssh_known_hosts
11.7 双因素认证(2FA)
11.7.1 启用 2FA
Gitea:
# 强制所有用户启用 2FA(管理后台配置)
# Administration → Authentication → 强制 2FA
# 或通过配置文件
# [security]
# TWO_FACTOR_AUTH = true
GitLab:
# /etc/gitlab/gitlab.rb
gitlab_rails['require_two_factor_authentication'] = true
gitlab_rails['two_factor_grace_period'] = 24 # 24 小时宽限期
11.7.2 2FA 与 SSH/Token
启用 2FA 后:
- SSH 密钥认证不受影响(推荐方式)
- HTTPS 认证需要使用 Personal Access Token 或 OAuth Token 代替密码
- Git Credential Manager 可以安全存储 Token
11.8 扩展阅读
本章小结
| 学到了什么 | 关键要点 |
|---|---|
| LDAP | 最常见的企业认证方式,支持 AD 和 OpenLDAP |
| OAuth2 | 适合第三方 IdP(Keycloak、GitHub、Google) |
| SAML | 企业 SSO 标准,推荐通过 Keycloak 桥接 |
| 2FA | 启用后需使用 Token 代替密码(SSH 不受影响) |
| Keycloak | 统一身份管理平台,支持 LDAP/OIDC/SAML 联合 |
下一章:第 12 章 - 仓库迁移 — 从 GitHub/GitLab 迁移仓库和历史清理。