Grafana 审计功能设计和实现

介绍

Grafana 提供了多租户和多用户的模式,还可以接入 OAuthLDAP 等认证方式,方便用户扩展。但有时候,需要对用户的一些查询行为进行审计,比如非法语句查询、挖掘高成本面板、控制租户查询配额等。Grafana 目前不支持详细的审计功能,为了在现有的版本上实现这个能力,需要做一些额外的工作。

设计

主要考虑 Prometheus 数据源的查询审计,其主要是 HTTP 的 API 请求,那么可以通过 Nginx 记录 Param 和 Body 来获取请求体,并且能够通过记录 HTTP 请求的 request timeresponse 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_tokenauth_tokenprev_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 来作为审计分析记录的存储。

最终完整的架构如下:

Grafana 审计功能设计和实现

成果

为了让 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 的请求可以在以下面板查看分析,并且可以得知查看的状态码、返回数据量、语句综合成本、查询用户等信息。

Grafana 审计功能设计和实现

也可以通过 rid ($request_id) 来跳转到具体的子查询,如下。

Grafana 审计功能设计和实现

加入一些基础的指标,可以观测分析程序的消耗非常的低。

Grafana 审计功能设计和实现

在写入 ClickHouse 时,使用 BatchInsert 的方式,可以减少分析流的阻塞,对 CK 而言性能也是最合适的。

Grafana 审计功能设计和实现


原文始发于微信公众号(AcidPool):Grafana 审计功能设计和实现

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/171041.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!