Nova Kwok's Awesome Blog

Grafana Basic Auth 趟坑小记

迁移 Grafana

本来其实是一个非常简单的需求,我需要把自己一个很早以前手动用包管理器安装的 Grafana/InfluxDB 套件迁移到另一个主机上,并且改为纯 Docker 部署并且重新制作面板(丢掉之前的数据),因为不需要考虑之前的数据了,所以迁移流程非常简单,先做一个简单的 docker-compose.yml 文件,内容大概如下:

version: "3"
services:

  influxdb:
    image: influxdb
    container_name: influxdb
    ports:
      - "192.168.1.3:8086:8086"
    restart: always
    volumes:
      - ./data/influxdb:/var/lib/influxdb

  grafana:
    image: grafana/grafana
    container_name: grafana
    ports:
      - "127.0.0.1:3000:3000"
    restart: always
    volumes:
      - ./data/grafana:/var/lib/grafana
    environment:
      - GF_SERVER_DOMAIN=internal.yyyy.xxx
      - GF_AUTH_DISABLE_LOGIN_FORM=false
      - GF_SERVER_ROOT_URL=https://internal.yyyy.xxx/grafana/
      - GF_SERVER_SERVE_FROM_SUB_PATH=true

关于底下的 environment ,可以参考 Configuration | Grafana Labs,简单来说就是,如果配置中是:

# default section
instance_name = ${HOSTNAME}

[security]
admin_user = admin

[auth.google]
client_secret = 0ldS3cretKey

[plugin.grafana-image-renderer]
rendering_ignore_https_errors = true

那么对应到环境变量上就是:

GF_DEFAULT_INSTANCE_NAME=my-instance
GF_SECURITY_ADMIN_USER=owner
GF_AUTH_GOOGLE_CLIENT_SECRET=newS3cretKey
GF_PLUGIN_GRAFANA_IMAGE_RENDERER_RENDERING_IGNORE_HTTPS_ERRORS=true

启动好容器之后就是一点 Nginx 的配置,大概是这样的:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate /path/to/ssl.pem;
    ssl_certificate_key /path/to/ssl.key;
    server_name internal.yyy.xxx;

    # 这里有个其他的服务,服务没有登录功能,需要用 Nginx 的 Basic Auth 保护一下
    location /service-a {
      auth_basic "Restricted Content";
      auth_basic_user_file /etc/nginx/htpasswd;
      proxy_pass http://192.168.1.7:8888;
    }

    location /grafana/ {
      proxy_pass http://localhost:3000/;
    }

}

看上去没有任何问题,然后访问一下 URL,就会看到:"invalid username or password"

啊这?我这还没输入密码呢?!

在继续前,大伙可以停下来,先根据上面的配置信息想想可能是哪儿出了问题.

Grafana 的验证方式

在看了一个看上去和我情况相似的 Issue 之后发现,在 User Authentication Overview,中,Grafana 支持多种验证方式,最常见的就是简单的用户名密码的方式,此外还有 SAML,LDAP 啥的:

但是如果足够细心往下看,就会发现有个叫 Basic authentication 的小节,内部是这样说明的:

Basic auth is enabled by default and works with the built in Grafana user password authentication system and LDAP authentication integration.

To disable basic auth:

[auth.basic]
enabled = false

这个时候问题就已经比较明了了,由于这个 internal.yyyy.xxx 还反代了一个其他服务,也就是上文中的:

location /service-a {
  auth_basic "Restricted Content";
  auth_basic_user_file /etc/nginx/htpasswd;
  proxy_pass http://192.168.1.7:8888;
}

正好我的浏览器之前有登录过那个服务,浏览器便给整个 internal.yyyy.xxx 域名的访问都加上了针对那个服务的 Authorization: Basic xxxxxxx 的 Header,正好 Nginx 把这个 Header 也传给了 Grafana,加上 Grafana 优先通过这个 Header 来验证用户,就有了上面那一出 "invalid username or password" 的问题了。

然后解决方法也比较明了了,只需要在 docker-compose.yml 的 Grafana 下环境变量中加入一条:

- GF_AUTH_BASIC_ENABLED=false

这样 Grafana 就不会关注那个不是给他看的 Header 了。

为这么个问题折腾了这么久,主要原因还是因为他(我)们(没)文(看)档(文)没(档)有写好,不应该不应该,记录此文,希望可以帮到其他可能在这里踩坑的同学。

References

  1. Grafana with htpasswd #8314
  2. User Authentication Overview
  3. Configuration | Grafana Labs

#Chinese #Grafana