• 正文
  • 相关推荐
申请入驻 产业图谱

Grafana | 实战教程:对接自有 OAuth2 身份验证服务,轻松实现单点登录(SSO)

05/26 07:24
347
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

如何在 Grafana 中接入内部 Oauth2 身份验证服务实现 SSO 单点登陆?

前言简述:

作为一名资深的运维工程师,随着云原生不断发展,作者部署了一系列开源的监控告警可视化系统,例如 Grafana、Nightingale 等,作为最常用的一些运维平台,在常规时需要使用不同的账户密码登陆,然而由于内部业务系统越来越多,开发、运维、测试、或者是领导使用的时候,需要为其分别创建账户密码,人少的时候还行,若涉及人员过多一个一个去创建管理也是一件麻烦事(耗费精力),所以为了统一管理这些系统的用户认证和权限控制,作者希望能够将这些系统接入到一套内部 Oauth2 身份验证系统中,从而实现单点登录(SSO)和统一的权限管理。

作者在采用 Golang + Gin + Gorm + go-oauth2 开发搭建了一套基于内部办公平台的 Oauth2 身份验证系统服务,现在需要将这套系统对接入到 Grafana 中,以解决相关运维系统统一登陆认证问题,遂记录一下实践操作,帮助后续遇到相同需求的道友。

环境说明

作者将前面在 Kubernetes 中容器化安装部署的 Grafana 从 12.0.2 版本升级到最新的 13.0.1-security-01 版本,以获得最新的功能特性和解决一些已知的安全问题,对于如何安装及升级,各位看友请参考知识星球中的相关文章。

[Grafana | 开源的可视化平台初识到云原生环境下部署实践]: https://articles.zsxq.com/id_gkzolgtmxflo.html[Grafana | 如何将现11.5.x升级到最新版本12.0.x?]:https://articles.zsxq.com/id_uhwlxi0xrw6j.html

另外在更新 Granfana 作者补充一点,若你也像作者一样使用容器化部署,且主机无法连接互联网来更新插件的话,你可以通过以下方式来解决:

# 步骤 01.在内部找一台可联网的主机,使用 软件源 或 Docker 部署 squid 代理,假设:作者此处部署的地址为 http://192.168.12.2:3128
# Docker | 运维:快速部署Squid 网络代理服务:
# https://articles.zsxq.com/id_212k4eje1ztu.html

# 步骤 02.在 Grafana 容器中配置代理,编辑 Grafana 的启动命令或环境变量以使用 squid 作为 HTTP_PROXY。例如:
kubectl edit deployments.apps -n dashboard grafana-server
....
  spec:
      containers:
      - env:
        - name: TZ
          value: Asia/Shanghai
        - name: https_proxy                # 环境变量
          value: http://192.168.12.2:3128  # 配置代理
        image: hgrafana/grafana:13.0.1-security-01

# 步骤 03.等待 Grafana 重启服务完毕,进入到容器之中,验证是否可联网然后手动更新插件,例如:
grafana cli plugins update-all

 

实践操作

温馨提示:在开始之前,请确保你已经有一个可用的 OAuth2 身份验证服务(例如 Keycloak、Auth0、自建等),并且已经创建了相应的客户端应用程序,以获取了必要的客户端 ID 和密钥,另外 Grafana 缺省回调地址为 http://<your-grafana-url>/login/generic_oauth,需要在 Oauth2 服务中配置回调地址,否则无法还回 Code 给应用,自然也无法换 Token。

weiyigeek.top-创建客户端以使用自建的OAuth2服务图

步骤 01.登录 Grafana,进入 Configuration|管理 -> Authentication|身份验证 页面,由下图可知在最新的 13.0.1 版本中,Grafana 支持多种身份验证方式,包括 OAuth2(GitHub、GitLab、Google、Generic OAuth、Entra ID、Okta)、LDAP、SAML/SCIM( Cloud 与 Enterprise 版本特有,也是 12.x 后新增加的) 等。

 

步骤 02.选择 Generic OAuth 作为身份验证方式,并填写你的 OAuth2 提供商的相关信息,主要包括:一般设置、用户映射、以及额外的安全措施等。

首先是一般设置,例如:

    • 显示名称:将在登录页面上显示为“使用… 登录”。如果您使用多个身份提供程序或 SSO 协议,这将很有帮助。• Client ID *:OAuth2 应用的Client ID。• Client secret:OAuth2 应用的Client secret。• 身份验证样式:它决定了如何将“Client ID”和“Client secret”发送到 Oauth2 提供程序。默认值为 AutoDetect,根据实际情况选择,如果自建的 OAuth2 服务,只接受 Header 方式传递,则选择 Header 方式。• Auth URL *:OAuth2 提供程序的授权端点。• Token URL *:OAuth2 提供程序的令牌端点。• API URL:OAuth2 提供商的用户信息端点。此端点返回的信息必须与 OpenID UserInfo 兼容。• 允许注册:如果未启用,则只有现有 Grafana 用户可以使用 OAuth 登录。请根据实际情况选择。• 退出登录重定向网址:从 Grafana 退出登录后将用户重定向到的网址。(可选)

weiyigeek.top-一般设置图

步骤 03.在进行用户映射配置,此处主要根据上一步中的 API URL 返回的用户信息,来映射到 Grafana 的用户属性上,注意 Grafana 建议使用 OpenID Connect UserInfo endpoint 规定的字段进行返回,例如:

OpenID Connect 指定了一组关于最终用户的标准声明,其中涵盖了常见的个人资料信息,如姓名、联系方式、出生日期和地区,标准 OpenID Connect 格式声明如下:

# sub {string} The subject (end-user) identifier. This member is always present in a claims set.
[ name ] {string} The full name of the end-user, with optional language tag.
[ given_name ] {string} The given or first name of the end-user, with optional language tag.
[ family_name ] {string} The surname(s) or last name(s) of the end-user, with optional language tag.
[ middle_name ] {string} The middle name of the end-user, with optional language tag.
[ nickname ] {string} The casual name of the end-user, with optional language tag.
[ preferred_username ] {string} The username by which the end-user wants to be referred to at the client application.
[ profile ] {string} The URL of the profile page for the end-user, with optional language tag.
[ picture ] {string} The URL of the profile picture for the end-user.
[ website ] {string} The URL of the end-user’s web page or blog.
[ email ] {string} The end-user’s preferred email address.
[ email_verified ] {true|false} True if the end-user’s email address has been verified, else false.
[ gender ] {“male”|“female”|?} The end-user’s gender.
[ birthdate ] {string} The end-user’s birthday, represented in ISO 8601:2004 YYYY-MM-DD format. The year may be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed.
[ zoneinfo ] {string} The end-user’s time zone, e.g. Europe/Paris or America/Los_Angeles.
[ locale ] {string} The end-user’s locale, represented as a BCP47 language tag. This is typically an ISO 639-1 Alpha-2 language code in lowercase and an ISO 3166-1 Alpha-2 country code in uppercase, separated by a dash. For example, en-US or fr-CA.
[ phone_number ] {string} The end-user’s preferred telephone number, typically in E.164 format, for example +1 (425) 555-1212 or +56 (2) 687 2400.
[ phone_number_verified ] {true|false} True if the end-user’s telephone number has been verified, else false.
[ address ] {object} A JSON object describing the end-user’s preferred postal address with any of the following members:
    [ formatted ] {string} The full mailing address, with multiple lines if necessary. Newlines can be represented either as a rn or as a n.
    [ street_address ] {string} The street address component, which may include house number, stree name, post office box, and other multi-line information. Newlines can be represented either as a rn or as a n.
    [ locality ] {string} City or locality component.
    [ region ] {string} State, province, prefecture or region component.
    [ postal_code ] {string} Zip code or postal code component.
    [ country ] {string} Country name component.
[ updated_at ] {number} Time the end-user’s information was last updated, as number of seconds since the Unix epoch (1970-01-01T0:0:0Z) as measured in UTC until the date/time.

另外,作者 userinfo endpoint 返回的 JSON 字段如下所示:

	c.JSON(http.StatusOK, gin.H{
"sub":          uu.Id,
"user_id":      uu.Number,
"user":         uu.UserName,
"name":         uu.Name,
"given_name":   firstname,
"family_name":  lastname,
"phone_number": uu.Mobile,
"email":        uu.Email,
"group":        uu.GroupId,
	})

weiyigeek.top-userinfo endpoint信息图

然后,根据实际情况,配置用户映射,如作者使用 name 字段作为用户名称,使用 user 作为登陆用户名,使用 email 作为用户邮箱,当然如果还返回了角色 role 字段(Admin、Editor、Viewer)的情况下还可配置角色属性路径参数,否则应该启用跳过组织角色同步,防止从您的 IdP 同步用户的组织角色。

weiyigeek.top-用户映射图

步骤 04.最后再进行额外的安全措施,主要限定通过 Oauth2 认证用户的组,以及允许那些邮箱域名进行注册登陆,例如:weiyigeek.top 表示只有邮箱公司域名 xxxxx@weiyigeek.top 才能访问;另外,如果自建的 OAuth2 服务启用了 https 但是自签证书的,请一定要设置 跳过 TLS 验证;其它参数请根据需要配置。

weiyigeek.top-额外的安全措施图

步骤 05.设置完毕后,便可保存配置,新打开一个浏览器访问 Granana 此时会发现首页多了一行 Sign in with 统一登陆认证平台 。

weiyigeek.top-Sign in with 统一登陆认证平台图

步骤 06.点击统一登陆认证平台后未认证用户将调转到统一认证登陆界面,若已经登陆了内部统一认证登陆界面则会自动登陆到 Grafana 从而实现类似于 SSO 的功能。

weiyigeek.top-统一登陆认证平台

步骤 07.认证成功后认证服务器将会把 code 参数传递 Grafana 回调地址,然后 Grafanan 后端再将此 code 带入请求 token_url 从而获取 Token,之后在携带获取的 Token 访问配置的 api_url (即用户信息端点),从而实现自动登陆,管理员可通过 >> 管理 >> 用户和访问权限 >> 用户 ,查看到那些用户是通过 Generic OAuth 认证。

weiyigeek.top-查看所有用户图weiyigeek.top-认证用户传递映射绑定的个人信息查看图

除此之外,我们还可直接 在 Grafana 的配置文件(通常是 grafana.ini)中启用 配置 OAuth2 提供者示例如下。

[auth.generic_oauth]
enabled = true
name = 统一登陆认证平台
client_id = 019e43e1-f4ad-7675-b119-62c9o44fa195
client_secret = your-client-secret
scopes = openid email profile
auth_url = https://auth.weiyigeek.top/oauth/authorize
token_url = https://auth.weiyigeek.top/oauth/token
api_url = https://auth.weiyigeek.top/api/v1/general/userinfo
signout_redirect_url = https://auth.weiyigeek.top/logout?redirect_uri=https://sec.weiyigeek.top/
allow_sign_up	true
auto_login	false
allow_assign_grafana_admin	false
name_attribute_path = sub
email_attribute_path = email
login_attribute_path = user
skip_org_role_sync	true
# role_attribute_path = contains(roles,'admin') && 'Admin' || contains(roles,'editor') && 'Editor' || 'Viewer'
allowed_domains weiyigeek.top
tls_skip_verify_insecure	true

至此,作者在生产环境中实践如何在 Grafana 中使用内部 OAuth2 服务对接认证从而实现内部运维组和开发测试组的同事自行注册访问,再也不用每个平台都去记一堆密码了。

最后,若需要自建的 OAuth2 服务的看友,可加入到作者【全栈工程师修炼指南】知识星球中,作者将提供相关参考资源和源码以及答疑支撑,另外最新的技术文章也是最先在星球中发布,最后感谢各位看友支持作者创作更多实用的文章,

加入:作者知识星球

相关推荐