介绍
Grafana 提供了多租户和多用户的模式,还可以接入 OAuth
和 LDAP
等认证方式,方便用户扩展。但有时候,需要对用户的一些查询行为进行审计,比如非法语句查询、挖掘高成本面板、控制租户查询配额等。Grafana 目前不支持详细的审计功能,为了在现有的版本上实现这个能力,需要做一些额外的工作。
设计
主要考虑 Prometheus 数据源的查询审计,其主要是 HTTP 的 API 请求,那么可以通过 Nginx 记录 Param 和 Body 来获取请求体,并且能够通过记录 HTTP 请求的 request time
和 response time
来计算查询耗时,以及 QPS 等关键指标。
为了将具体的请求管理到执行用户,还需要暴露出 Grafana 的用户信息。首先是 Org 信息,这个可以在 HTTP Header X-Grafana-Org-Id
获取,Grafana 内部也是使用这个 Header 来区分的。至于用户名称,这一点其实 Grafana 是支持的,通过开启配置 send_user_header
来启用,之后 Grafana 会在 HTTP Header 的 X-Grafana-User
中记录用户的信息。但是,这个功能在很大一个版本跨度里存在问题,具体的 issue 可参考#54159,官方在后续也提供了 fix。但如果内部的 Grafana 无法升级到指定的版本,可以通过解码 Cookie 和 Token 的方式来实现类似功能。
Cookie
获取 Grafana 的 Session Cookie 字段,根据 Grafana 源码的处理方式,得到数据库内的索引。
func HashToken(token string) string {
hashBytes := sha256.Sum256([]byte(token + grafanaSec))
return hex.EncodeToString(hashBytes[:])
}
grafanaSec
是 Grafana 的 SecretKey
,因此需要从配置里获取,然后再将哈希值与数据表 user_auth_token
的 auth_token
或 prev_auth_token
对比,来匹对到具体用户。
Basic Token
创建的内部账号会使用此类认证,例如一开始的 admin 账户。这种可以直接使用 base64.RawURLEncoding.DecodeString(token string)
解码,然后根据 :
分割获取第一位的用户名。
Bearer Token
大部分的 OAuth 的认证都是使用 JWT 的方式,将 Token Unmarshal
到以下结构体,通过 Name
字段可以获取用户信息。
type rawToken struct {
Key string `json:"k"`
Name string `json:"n"`
OrgID int `json:"id"`
}
但是需要注意,因为将 Token 记录到了 Nginx,需要额外分流这部分的 access 日志,并缩小权限。
最后的问题,就是关联到数据源了,因为 Grafana 请求的数据源地址,除了使用浏览器直接访问外,内部都是通过 Proxy 的方式去请求,必须获取到 Proxy ID
和代理数据源的映射,这一步,则需要查询 Grafana 的数据库来获取。
当然,这类用户和数据源配置往往改动频率非常的小,为了减少对 DB 的负载,可以加上一次 Cache。
上面的分析操作,需要额外构建一个小的程序来实现了,与代理 Grafana 的 Nginx 以 SideCar
的方式部署,需要存储分析结果和支持大规模的检索计算能力,可以使用 ClickHouse 来作为审计分析记录的存储。
最终完整的架构如下:
成果
为了让 Nginx 的分离以实现管理,并且控制落盘频率,避免大请求体占用 IO,将 Grafana 的日志拆分。
location /grafana/ {
access_log /var/log/nginx/access_grafana.log grafana buffer=3m flush=30s;
proxy_set_header Host $http_host;
proxy_pass http://grafana/;
}
输出的日志格式则将需要的字段从 Nginx 暴露,最好可以将 $request_body
两侧添加如 ##
一些特殊符号,方便解析。
log_format grafana escape=none '[$time_local] "$request_id" "$request_method" $status '
'"$request_uri" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$upstream_response_time $upstream_addr "$host" '
'"$http_x_grafana_user" "$http_x_grafana_org_id" '
'"$cookie_grafana_session" "$http_authorization" '
'##$request_body##';
这里可以借助 $request_id
来关联由此下发的子 Pannel 的查询,做到从 Pannel 到 Dashboard 的统计。
每一次 Dashboard 的请求可以在以下面板查看分析,并且可以得知查看的状态码、返回数据量、语句综合成本、查询用户等信息。
也可以通过 rid ($request_id
) 来跳转到具体的子查询,如下。
加入一些基础的指标,可以观测分析程序的消耗非常的低。
在写入 ClickHouse 时,使用 BatchInsert
的方式,可以减少分析流的阻塞,对 CK 而言性能也是最合适的。
原文始发于微信公众号(AcidPool):Grafana 审计功能设计和实现
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/171041.html