跳到主内容

使用 Casdoor 实现 Jenkins OIDC、Gitea OIDC 和 Grafana SSO:为自托管 DevOps 技术栈提供统一登录

· 阅读时间:一分钟
Yang Luo
Casdoor Maintainer

典型自托管 DevOps 技术栈中的每个工具都可以作为 OpenID Connect 客户端:Gitea 和 Argo CD 原生支持 OIDC,Jenkins 通过 oic-auth 插件支持,Grafana 和 Portainer 通过通用 OAuth 2.0 支持,MinIO、Harbor 和 GitLab 则通过其 OIDC 设置支持。将所有工具指向同一个 Casdoor 实例,为每个工具分配一个独立的 Casdoor 应用,并将用户添加到 Casdoor 群组中。随后,每个工具读取 groups 声明并确定谁是管理员。本文列出了每个工具的回调 URL、配置键和群组映射,然后介绍了它们的共同部分。

技术栈概览​

以下所有路径均相对于该工具自身的公共 URL。请在对应工具的 Casdoor 应用的 Redirect URLs 中注册这些路径。

应用协议回调 URL群组/角色到管理员映射Casdoor 文档
GiteaOIDC(OAuth2 认证源)/user/oauth2/<source-name>/callback是:管理员群组、受限群组、团队映射Gitea
JenkinsOIDC(oic-auth 插件)/securityRealm/finishLogin,登出 /OicLogout是:groups 声明加上基于矩阵的安全策略Jenkins (OIDC)
Grafana通用 OAuth 2.0/login/generic_oauth是:role_attribute_pathGrafana
Argo CDOIDC/auth/callback,CLI http://localhost:8085/auth/callback是:argocd-rbac-cmArgo CD
MinIOOIDC控制台 /oauth_callback从一个声明获取策略 (claim_name)MinIO
PortainerOAuth 2.0(自定义)Portainer 基础 URL仅商业版Portainer
HarborOIDC/c/oidc/callback是:OIDC Admin Group暂无
GitLabOIDC(OmniAuth)/users/auth/openid_connect/callbackPremium 和 Ultimate 版本:admin_groupsGitLab

每个 OIDC 客户端所需的发现文档位于 https://<casdoor-host>/.well-known/openid-configuration。如果您之前未将 OIDC 客户端连接到 Casdoor,连接标准 OIDC 客户端 解释了相关端点。

一次性准备 Casdoor​

在操作各个工具之前,请先在 Casdoor 中设置好以下三项内容。

Groups. 在组织树中创建诸如 devops-admins 和 devops-users 的群组,并将用户添加到其中。参见 Groups。

Token group format. 默认情况下,groups 声明将每个群组命名为 <organization>/<group>。大多数工具将该声明与您输入的纯字符串进行比较,因此请将每个应用的 OIDC/OAuth 选项卡下的 Token group format 设置为 Name (group)。这样声明中将包含 devops-admins。相关选项说明请见 Group format。

Token format. 保持 Token format 为默认的 JWT。在此格式下,ID 令牌包含 groups、roles 和 tag。JWT-Empty 和 JWT-Standard 不会在令牌中包含这三项,而 JWT-Custom 仅在您在 Token fields 中选择它们时才包含。当授予 profile scope 时,/api/userinfo 响应会返回 groups 和角色名称,因此读取 userinfo 的工具仍然可以看到这些信息。

角色同样适用。在 JWT 格式下,roles 声明是一个角色对象数组,因此请匹配 roles[*].name。在 /api/userinfo 中,它是一个纯名称数组。参见 User roles。

Gitea​

在 Site Administration 中,添加一个类型为 OAuth2、提供商为 OpenID Connect 的认证源,填入客户端 ID 和密钥以及 Casdoor 发现 URL。您为该源命名的名称是回调 URL 的一部分:名为 casdoor 的源使用 https://gitea.example.com/user/oauth2/casdoor/callback。

对于群组映射,请在 Claim name providing group names 中填入 groups,然后在 Group Claim value for administrator users 中填入 devops-admins。Gitea 还有 Group Claim value for restricted users 和 Map claimed groups to Organization teams。以下是通过 CLI 配置同一源的示例:

gitea admin auth add-oauth \
--name casdoor \
--provider openidConnect \
--key <client-id> \
--secret <client-secret> \
--auto-discover-url https://door.example.com/.well-known/openid-configuration \
--group-claim-name groups \
--admin-group devops-admins

Jenkins​

Jenkins 本身不支持 OIDC。安装 OpenId Connect Authentication 插件,将其选为安全域,并使用 well-known 配置。登录回调是 /securityRealm/finishLogin,登出回调是 /OicLogout;请将两者都添加到 Casdoor 中。在 JCasC 中:

jenkins:
securityRealm:
oic:
serverConfiguration:
wellKnown:
wellKnownOpenIDConfigurationUrl: https://door.example.com/.well-known/openid-configuration
clientId: <client-id>
clientSecret: <client-secret>
userNameField: name
fullNameFieldName: displayName
emailFieldName: email
groupsFieldName: groups
logoutFromOpenidProvider: true
properties:
- "pkce"

该插件仅将群组传递给 Jenkins。权限来自授权策略:在使用基于矩阵的安全策略时,表格的每一行代表一个用户或群组,因此请为 devops-admins 添加一行并赋予其 Overall/Administer 权限。选择该插件作为安全域会禁用其他登录方法,因此在保存前请配置插件的 escapeHatch 属性(备用用户名和密钥)。Casdoor 也有自己的 Jenkins 插件,详见 Jenkins plugin,但本文假设使用的是通用 OIDC 插件。

Grafana​

Grafana 使用通用 OAuth。其回调地址为 /login/generic_oauth。Grafana 的默认 scopes 是 user:email,因此请显式设置 OIDC scopes:

[auth.generic_oauth]
enabled = true
name = Casdoor
client_id = <client-id>
client_secret = <client-secret>
scopes = openid profile email
auth_url = https://door.example.com/login/oauth/authorize
token_url = https://door.example.com/api/login/oauth/access_token
api_url = https://door.example.com/api/userinfo
use_pkce = true
groups_attribute_path = groups
role_attribute_path = contains(groups[*], 'devops-admins') && 'Admin' || contains(groups[*], 'devops-users') && 'Editor' || 'Viewer'
role_attribute_strict = true

Grafana 首先针对 ID 令牌评估 role_attribute_path,然后是 userinfo 响应,最后是访问令牌。有效的结果为 None、Viewer、Editor、Admin 和 GrafanaAdmin;最后一种需要设置 allow_assign_grafana_admin = true。allowed_groups 限制仅列出的群组可以登录,并且需要 groups_attribute_path。团队同步是 Grafana Enterprise 和 Grafana Cloud 的功能。

Argo CD​

Argo CD 直接与 Casdoor 通信,而不使用其捆绑的 Dex。浏览器回调地址为 /auth/callback。argocd login --sso 默认在端口 8085 上启动本地服务器,并使用 http://localhost:8085/auth/callback,因此也需要注册该地址。

在 argocd-cm 中设置 url 和 oidc.config(包含 name、issuer、clientID、clientSecret、requestedScopes)。Argo CD 默认的 requestedScopes 添加了 groups 范围。Casdoor 不需要此范围,因为 JWT ID 令牌始终携带 groups,所以我们的指南请求 ["openid", "profile", "email"]。群组映射在 argocd-rbac-cm 中进行,其中 scopes 默认为 [groups]:

data:
policy.default: role:readonly
policy.csv: |
g, devops-admins, role:admin

完整的演练步骤,包括 argocd-secret 中的客户端密钥,请参见 Argo CD 指南。

MinIO​

MinIO 从单个声明读取策略,通过 claim_name 设置(默认为 policy)。它不映射群组。MinIO 自己的 Casdoor 快速入门指南和我们的指南都使用 Casdoor 的 tag 声明:将 MinIO 策略名称(如 readwrite)放入用户的 Tag 中,然后:

mc admin config set myminio identity_openid \
config_url="https://door.example.com/.well-known/openid-configuration" \
client_id=<client-id> client_secret=<client-secret> claim_name="tag"

role_policy 是另一种选择:通过此提供商登录的所有用户都会获得列出的策略,且不能与 claim_name 结合使用。控制台回调地址为 <console-url>/oauth_callback。在代理后面时,请设置 MINIO_BROWSER_REDIRECT_URL;旧的 redirect_uri 键已弃用。请注意,minio/minio GitHub 仓库已被归档,其 README 指出不再维护,因此在依赖这些设置之前,请检查您运行的 MinIO 构建版本。

Portainer​

Portainer 使用自定义 OAuth 提供商。在您的 Casdoor 主机上填写 Authorization URL (/login/oauth/authorize)、Access token URL (/api/login/oauth/access_token) 和 Resource URL (/api/userinfo),并将 Redirect URL 设置为 Portainer 基础 URL。还有一个 Logout URL 字段。在 Portainer CE 中,Automatic team membership 被标记为 Business Edition 功能,而将管理员权限分配给群组的设置也仅在 Business Edition 中提供。在 CE 版本中,自动配置创建的用户将获得标准用户角色,您需要手动提升管理员。

Harbor 和 GitLab​

Harbor 在 OIDC 设置页面底部显示其重定向 URI:<harbor-url>/c/oidc/callback。将 OIDC Provider Endpoint 设置为 Casdoor URL,Group Claim Name 设置为 groups,OIDC Admin Group 设置为 devops-admins。Harbor 从 ID 令牌读取群组声明,这由 JWT 格式提供。只有在存在任何本地用户之前,才能将 Harbor 从数据库认证切换到 OIDC 认证。

GitLab 的 OIDC 文档有一个 Casdoor 部分,并要求 Casdoor 通过 HTTPS 提供服务。回调地址为 /users/auth/openid_connect/callback。基于群组的角色(groups_attribute、admin_groups、required_groups、external_groups)需要 GitLab Premium 或 Ultimate 版本。

不支持 OIDC 的工具​

对于没有自身登录功能的内部仪表板,将它们置于 Apache APISIX 的 openid-connect 插件之后(discovery、client_id、client_secret、redirect_uri、bearer_only),如 APISIX 指南 所述,或者置于 nginx 之后,如 Nginx SSO 认证 所述。

每个工具一个应用还是共享一个应用​

使用单个 Casdoor 应用并将所有工具的回调地址放入其 Redirect URLs 是可以工作的,但每个工具一个应用更容易运行:

  • 每个工具都有自己的客户端密钥,因此泄露的 Jenkins 密钥不会暴露 Grafana。
  • Token group format、Token format 和令牌过期时间是按应用配置的,因此您可以为 Argo CD 设置 Name (group) 而不改变其他工具的声明。
  • Casdoor 会根据应用的 Redirect URLs 检查 post_logout_redirect_uri,因此每个列表保持简短且具体。

用户仍然只需登录一次:首次登录后,Casdoor 的会话 Cookie 允许下一个工具的重定向完成而无需密码提示。

登出​

Casdoor 的结束会话端点是 /api/logout,在发现文档中列为 end_session_endpoint。它接受 id_token_hint、post_logout_redirect_uri、client_id 和 state,并且只重定向到应用 Redirect URLs 中列出的 URL。从 Casdoor 登出会结束 Casdoor 会话以及该会话的令牌,但每个工具也会保留其自身的会话 Cookie。在每个工具中配置登出:

  • Jenkins:logoutFromOpenidProvider,并注册 /OicLogout。
  • Grafana:signout_redirect_url。
  • Argo CD:oidc.config 中的 logoutURL,例如 https://door.example.com/api/logout?id_token_hint={{token}}&post_logout_redirect_uri={{logoutRedirectURL}}。
  • Harbor:OIDC Session Logout。
  • Portainer:Logout URL。

端点的详细信息请参见 RP-initiated logout。

参考资料​