Grafana集成Authentik ODIC单点登录

Grafana 支持 Generic OAuth,可以直接对接 Authentik 的 OAuth2/OpenID Connect Provider。配置过程本身并不复杂,但有两个很容易踩到的坑:

  1. 没有配置 login_attribute_path 时,Grafana 可能把 email 当作登录名。
  2. OIDC 角色映射为 Admin 时,用户只是 Organization Admin,并不具备安装插件所需的 Server Admin 权限。

本文使用示例参数,完整说明安装、OIDC 对接、Secret 管理、用户名映射、管理员权限和常见问题。

1. 示例参数

本文统一使用以下占位参数:

参数示例值
Grafana 地址https://grafana.example.com
Authentik 地址https://auth.example.com
Kubernetes namespacemonitoring
OIDC Secretgrafana-oidc
Authentik 管理员组Grafana Server Admins
Authentik Provider sluggrafana

请根据实际环境替换域名、namespace、组名、版本和镜像 digest。不要把真实 client secret、用户名、邮箱、内网 IP 或集群信息写入公开文章或 Git 仓库。

2. 在 Authentik 中创建 Provider

在 Authentik 管理后台创建 OAuth2/OpenID Provider。

建议参数:

配置项建议值
Client typeConfidential
Redirect URIhttps://grafana.example.com/login/generic_oauth
Scopesopenid email profile
Signing Key选择可用的签名证书
Subject mode使用稳定且不会随用户名变化的用户标识

Redirect URI 必须和 Grafana 实际回调地址完全一致,包括协议、域名和路径。

随后创建 Authentik Application,并关联刚才创建的 Provider。

3. 确认 OIDC claims

Grafana至少需要以下 claims:

{
 "sub": "stable-user-id",
 "preferred_username": "alice",
 "email": "alice@example.com",
 "name": "Alice",
 "groups": ["Grafana Server Admins"]
}

各字段用途:

  • sub:外部身份的稳定唯一标识。
  • preferred_username:Grafana login。
  • email:Grafana email。
  • name:显示名称。
  • groups:用于角色映射,必须是数组。

如果 UserInfo 中没有 preferred_usernamegroups,需要在 Authentik Provider 中检查 profile scope 和 Property Mapping。

4. 使用 Kubernetes Secret 保存 OIDC 凭据

不要把 client_idclient_secret 写进 Helm values、ConfigMap 或 Git。

先交互输入凭据,避免 secret 直接进入 shell history:

printf 'Authentik client_id: '
IFS= read -r GRAFANA_OIDC_CLIENT_ID

printf 'Authentik client_secret: '
IFS= read -rs GRAFANA_OIDC_CLIENT_SECRET
printf '\n'

创建 Kubernetes Secret:

kubectl create namespace monitoring --dry-run=client -o yaml \
| kubectl apply -f -

kubectl -n monitoring create secret generic grafana-oidc \
 --from-literal=GF_AUTH_GENERIC_OAUTH_CLIENT_ID="$GRAFANA_OIDC_CLIENT_ID" \
 --from-literal=GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET="$GRAFANA_OIDC_CLIENT_SECRET" \
 --dry-run=client -o yaml \
| kubectl apply -f -

unset GRAFANA_OIDC_CLIENT_ID GRAFANA_OIDC_CLIENT_SECRET

只检查 Secret key,不输出值:

kubectl -n monitoring get secret grafana-oidc -o json \
| jq -r '.data | keys[]'

预期输出:

GF_AUTH_GENERIC_OAUTH_CLIENT_ID
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET

Kubernetes Secret 的 .data 只是 Base64 编码,并不等于加密。生产环境仍需要:

  • 限制 Secret RBAC。
  • 启用 etcd encryption at rest。
  • 避免在终端、CI 日志和排障记录中展开 Secret 值。
  • 使用 External Secrets、SOPS 或 Sealed Secrets 管理 GitOps 场景中的敏感信息。

5. Grafana Helm values

下面只展示与 Grafana 和 OIDC 相关的核心参数:

grafana:
enabled: true
replicas: 1

image:
  registry: docker.io
  repository: grafana/grafana
  tag: "<pinned-version>"
  sha: "<verified-sha256>"
  pullPolicy: IfNotPresent

deploymentStrategy:
  type: Recreate

envFromSecret: grafana-oidc

persistence:
  enabled: true
  type: pvc
  accessModes:
    - ReadWriteOnce
  size: 10Gi
  storageClassName: "<storage-class>"

ingress:
  enabled: false

grafana.ini:
  auth.generic_oauth:
    name: authentik
    enabled: true
    auto_login: true
    allow_sign_up: true
    allow_assign_grafana_admin: true
    scopes: openid email profile
    auth_url: https://auth.example.com/application/o/authorize/
    token_url: https://auth.example.com/application/o/token/
    api_url: https://auth.example.com/application/o/userinfo/
    login_attribute_path: preferred_username
    role_attribute_path: "contains(groups, 'Grafana Server Admins') && 'GrafanaAdmin' || 'Viewer'"

  users:
    allow_sign_up: false

  security:
    cookie_secure: true
    cookie_samesite: strict
    disable_gravatar: true

  server:
    domain: grafana.example.com
    root_url: https://grafana.example.com/
    enforce_domain: true

5.1 为什么使用 envFromSecret

Secret key 使用 Grafana 环境变量名称:

GF_AUTH_GENERIC_OAUTH_CLIENT_ID
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET

Grafana启动时通过环境变量读取 OIDC 凭据,values 和由 Helm 生成的 ConfigMap 中不需要出现明文 secret。

5.2 为什么使用 Recreate

当单副本 Grafana 使用 RWO PVC 时,RollingUpdate 可能先创建新 Pod。旧 Pod 尚未释放磁盘,新 Pod 就会发生 Multi-Attach。

因此单副本 RWO 场景建议:

deploymentStrategy:
type: Recreate

它会先删除旧 Pod,再创建新 Pod。代价是升级时存在短暂中断,但不会发生新旧 Pod 争抢同一块 RWO 存储的问题。

6. 用户名映射

Grafana Generic OAuth 会从 ID Token、UserInfo 和 Access Token 中寻找用户 login。如果没有找到合适字段,可能回退为 email。

显式配置:

login_attribute_path: preferred_username

这样 Grafana 会把 Authentik 的 preferred_username 作为 login,而不是把 email 显示成登录名。

需要同时确保:

scopes: openid email profile

并确认 Authentik确实返回:

{
 "preferred_username": "alice"
}

修改 claim 映射后,已有用户必须退出并重新登录,才会触发身份同步。

7. 管理员权限映射

Grafana 的 AdminGrafanaAdmin 不是同一个权限级别。

角色权限
Viewer查看 dashboard
Editor编辑 dashboard 等组织资源
AdminOrganization Admin
GrafanaAdminServer Admin,管理整个 Grafana 实例

安装、升级或删除插件需要 Server Admin。如果只映射为:

Admin

用户可能看到:

You do not have permission to install this plugin.

正确配置需要同时包含:

allow_assign_grafana_admin: true
role_attribute_path: "contains(groups, 'Grafana Server Admins') && 'GrafanaAdmin' || 'Viewer'"

生产环境建议创建专用的 Grafana Server Admin 组,不要直接复用范围过大的全局管理员组。

8. HTTPS 和回调地址

Grafana对外地址应使用 HTTPS:

server:
domain: grafana.example.com
root_url: https://grafana.example.com/
enforce_domain: true

同时启用安全 Cookie:

security:
cookie_secure: true
cookie_samesite: strict

无论使用 Ingress、Gateway API 还是其他 HTTPS 反向代理,都需要:

  • 保留原始 Host
  • 正确传递 X-Forwarded-Proto: https
  • 使用覆盖 Grafana 域名的有效证书。
  • 将 HTTP 请求跳转到 HTTPS。
  • 在 Authentik 中配置完全一致的 Redirect URI。

9. Helm 安装

固定 Chart 版本并下载到本地:

CHART_VERSION="<pinned-chart-version>"

helm repo add prometheus-community \
https://prometheus-community.github.io/helm-charts
helm repo update

helm pull prometheus-community/kube-prometheus-stack \
 --version "$CHART_VERSION" \
 --destination .cache/charts

对下载的 Chart 计算并人工核对 SHA256:

sha256sum ".cache/charts/kube-prometheus-stack-${CHART_VERSION}.tgz"

渲染和 lint:

helm template monitoring-stack \
 ".cache/charts/kube-prometheus-stack-${CHART_VERSION}.tgz" \
 --namespace monitoring \
 -f values.yaml \
> rendered.yaml

helm lint --strict \
 ".cache/charts/kube-prometheus-stack-${CHART_VERSION}.tgz" \
 -f values.yaml

安装:

helm upgrade --install monitoring-stack \
 ".cache/charts/kube-prometheus-stack-${CHART_VERSION}.tgz" \
 --namespace monitoring \
 --create-namespace \
 -f values.yaml \
 --wait \
 --timeout 30m

生产环境不要使用 latest,也不要在正式安装时重新按版本标签拉取未经当前变更审查的 Chart。

10. 登录验收

先检查 Pod:

kubectl -n monitoring get deployment,pod,pvc \
 -l app.kubernetes.io/name=grafana -o wide

检查健康状态:

curl -fsS https://grafana.example.com/api/health

预期包含:

{
 "database": "ok"
}

登录后确认:

  1. 登录名是 preferred_username,不是 email。
  2. email 字段仍正确显示。
  3. 管理员组成员拥有 Server Admin。
  4. 普通用户默认是 Viewer。
  5. OIDC client secret 没有出现在 ConfigMap 或 rendered YAML 中。

修改 claim、用户名或组以后,需要访问:

https://grafana.example.com/logout

然后重新登录。如果启用了 auto_login,Grafana会自动重新进入 Authentik 登录流程。

11. Secret 轮换

在 Authentik 中生成新 client secret 后,重新应用 Secret:

printf 'Authentik client_id: '
IFS= read -r GRAFANA_OIDC_CLIENT_ID

printf 'New Authentik client_secret: '
IFS= read -rs GRAFANA_OIDC_CLIENT_SECRET
printf '\n'

kubectl -n monitoring create secret generic grafana-oidc \
 --from-literal=GF_AUTH_GENERIC_OAUTH_CLIENT_ID="$GRAFANA_OIDC_CLIENT_ID" \
 --from-literal=GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET="$GRAFANA_OIDC_CLIENT_SECRET" \
 --dry-run=client -o yaml \
| kubectl apply -f -

unset GRAFANA_OIDC_CLIENT_ID GRAFANA_OIDC_CLIENT_SECRET

外部 Secret 更新通常不会自动刷新已有 Pod 的环境变量,因此需要执行:

kubectl -n monitoring rollout restart deployment/<grafana-deployment>
kubectl -n monitoring rollout status deployment/<grafana-deployment> \
 --timeout=10m

随后重新完成一次 OIDC 登录验收。

12. ConfigMap 还是 Helm values

生产环境推荐:

Helm values 保存非敏感配置
Kubernetes Secret 保存 OIDC 凭据
Helm 自动生成和管理 ConfigMap

不建议手工修改 Grafana ConfigMap,原因是:

  • Helm upgrade 可能覆盖手工修改。
  • 容易形成 values、rendered YAML 和 live ConfigMap 之间的配置漂移。
  • ConfigMap不能保存 client secret。
  • Helm values 更适合做 render、lint、diff 和 rollback。

因此,values 是配置源,ConfigMap 是 Helm 渲染产物,Secret 是敏感信息来源。

13. 常见问题

登录名仍然是 email

检查:

login_attribute_path: preferred_username

并确认 Authentik UserInfo 中存在 preferred_username。已有用户需要退出并重新登录。

用户是 Admin,但不能安装插件

Admin 只是 Organization Admin。需要:

allow_assign_grafana_admin: true
role_attribute_path: "contains(groups, 'Grafana Server Admins') && 'GrafanaAdmin' || 'Viewer'"

确认 groups claim 是数组,组名大小写和空格完全一致。

出现 invalid_client

检查:

  • client ID 和 client secret 是否来自同一个 Authentik Provider。
  • Secret key 名是否正确。
  • Secret 轮换后 Grafana Pod 是否已重启。
  • Provider 是否为 Confidential client。

出现 redirect_uri mismatch

检查 Authentik Redirect URI:

https://grafana.example.com/login/generic_oauth

以及 Grafana:

root_url: https://grafana.example.com/

修改配置后角色没有变化

Pod 重启只会加载新配置,不会自动刷新现有用户的 OAuth claims。用户必须退出并重新登录。

如果浏览器持续复用 Authentik 会话,可以:

  • 使用无痕窗口。
  • 在 Authentik 中撤销应用会话。
  • 同时退出 Authentik。

14. 关于插件安装

Server Admin 权限只代表用户有能力安装插件,不代表可以绕过供应链审查。

生产环境安装插件前仍应:

  1. 固定插件版本。
  2. 获取并验证官方 SHA256。
  3. 禁止使用 latest
  4. 优先离线缓存或预置插件。
  5. 在测试环境验证兼容性后再进入生产。

15. 参考资料

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
Istio云原生

基于Istio的微服务可观察性

2025-7-15 7:03:38

SkyWalking云原生

SkyWalking全链路监控

2025-4-9 8:41:37

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索
本站支持IPv6访问