原作者:张晋涛(MoeLove)。原文:《为 Prometheus Node Exporter 加上认证》,2020 年 5 月 26 日。本文经授权核查整理。案例使用 Node Exporter 1.0.0、Prometheus 2.18.1,原日志记录 Go 1.14.3;它是一份历史实践记录,不能原样视作现代部署指南。
Prometheus 的监控生态包括 Prometheus、Alertmanager、Node Exporter、Blackbox Exporter、Pushgateway 等组件。Node Exporter 收集节点 CPU、内存、磁盘和网络等系统信息。原文写作时,很多指标接口直接暴露,通常需要反向代理补充 TLS 或认证。Node Exporter 1.0.0 的原生能力使这类配置可以在 exporter 自身完成,这正是作者写文庆祝的原因。
指标不等于没有敏感信息:标签、主机名、挂载路径及系统状态都可能泄露内部结构。TLS 解决传输保护与服务端身份验证,Basic Auth 解决客户端凭据校验;它们承担不同职责。Basic Auth 本身不加密口令,不能用它替代 TLS。

第一步:准备证书
原作者在 prometheus-tls 目录下生成一对自签证书与私钥:
mkdir -p prometheus-tls
cd prometheus-tls
openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \
-keyout node_exporter.key -out node_exporter.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Moelove.info/CN=localhost"
生成文件分别为 node_exporter.crt 和 node_exporter.key。-nodes 表示私钥不使用口令加密,因此文件权限和宿主机访问控制尤其重要。原命令只给了 CN=localhost,没有 Subject Alternative Name(SAN)。Go 1.15 发布说明明确指出,缺少 SAN 时把 CN 当作主机名的旧行为已默认关闭。现代客户端不能靠沿用这份证书解决名称验证。
编者修订:如果仅用于本机隔离练习,可以签发同时包含 localhost 与 127.0.0.1 的 SAN 证书。下面新增 umask 077 与 SAN,缩短演示有效期;这是静态核对的改写,未实际生成证书。OpenSSL 版本须支持 -addext。远程部署应改用组织的证书和实际 DNS 名称,而不是复制此主机身份。
umask 077
openssl req -new -newkey rsa:2048 -days 30 -nodes -x509 \
-keyout node_exporter.key -out node_exporter.crt \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
第二步:让 Node Exporter 提供 HTTPS
原文从官方发布页取得 1.0.0 的 Linux amd64 压缩包,解压后进入目录:
tar -zxvf node_exporter-1.0.0.linux-amd64.tar.gz
cd node_exporter-1.0.0.linux-amd64
cp ~/prometheus-tls/node_exporter.* .
解压目录包含 node_exporter、LICENSE 和 NOTICE。下载及解压前应核对平台与发布校验信息,并只在独立目录处理可信压缩包。原文的通配复制会把匹配文件一并带入,实际部署宜明确指定证书与私钥,避免顺带复制备份或无关文件。
在该目录创建 config.yaml:
tls_server_config:
cert_file: node_exporter.crt
key_file: node_exporter.key
原文启动命令是下面这一条。它在 1.0.0 时是真实存在、且当时标为实验性的参数,不应反过来认定原文写错。
# 历史示例:Node Exporter 1.0.0
./node_exporter --web.config=config.yaml
编者补充:Node Exporter 1.0.0 默认监听 :9100,不是仅监听本机。上例在配置 Basic Auth 之前,即使启用了 TLS,其他网络可达主机仍可能读取指标。隔离练习应将启动参数增加为 --web.listen-address=127.0.0.1:9100,并仅由同机客户端访问;跨机生产采集则应明确绑定接口、配置防火墙或网络策略,把来源限制到所需的 Prometheus 实例,不能只依赖 HTTPS 隐藏端口。
作者日志显示版本 1.0.0、构建 Go 1.14.3,并出现 TLS is enabled and it cannot be disabled on the fly.。这是原作者当时的输出。核查时读取到的当前官方 README使用 --web.config.file=web-config.yml;升级时必须先核当前二进制帮助及配置文档,不能混用两代参数。
第三步:区分协议错误与证书错误
原作者依次测试了三种情况:
curl localhost:9100/metrics
# 原文返回:Client sent an HTTP request to an HTTPS server.
curl https://localhost:9100/metrics
# 原文返回:curl: (60) SSL certificate problem: self signed certificate
curl -s --cacert node_exporter.crt https://localhost:9100/metrics \
| grep node_exporter_build_info
第一种用 HTTP 访问 HTTPS 端口,协议不匹配。第二种协议正确,但系统不信任这张自签证书。第三种显式指定信任证书,作者得到了 node_exporter_build_info,其中 version="1.0.0"、goversion="go1.14.3"。这一步还需要名称与证书匹配;仅有 --cacert 并不能弥补 SAN 错误。
原文另展示 curl -s -k https://localhost:9100/metrics。-k 会跳过证书验证,只能在受控隔离诊断中用来区分问题,不得写入长期抓取配置或当作正式验收。本文没有执行这一命令。
第四步:配置 Prometheus 的 TLS 抓取
作者使用 Prometheus 2.18.1,把证书复制到它的目录。Prometheus 需要的是信任证书,不需要 Node Exporter 的服务端私钥。原文配置如下:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node_exporter'
scheme: https
tls_config:
ca_file: node_exporter.crt
static_configs:
- targets: ['localhost:9100']
scheme: https 切换抓取协议,tls_config.ca_file 指定信任材料。启动后查看 /targets:除了确认 endpoint 以 HTTPS 开头,还应检查目标状态、最近抓取时间和错误栏。原文第一张截图记录了 TLS 目标为 UP,不能把只看到 HTTPS 字样等同抓取成功。容器内的 localhost 指向容器自身,跨容器时还需要匹配实际服务地址与 SAN。
第五步:在服务端增加 Basic Auth
原文用 htpasswd 交互生成 bcrypt 哈希,成本参数为 12,没有在命令行传明文口令:
htpasswd -nBC 12 '' | tr -d ':\n'
随后把用户名和哈希加入 Node Exporter 的配置。以下保留原文公开哈希,它与原文的示例口令属于公开演示数据,不能用于真实服务:
tls_server_config:
cert_file: node_exporter.crt
key_file: node_exporter.key
basic_auth_users:
prometheus: $2y$12$WLw2sYa.NYZoBVoCOE84qe3xNm7kbSoKVIBXP.PvqNDna60vnZhEW
作者重启 Node Exporter 后,无凭据请求得到 401 Unauthorized 和 Www-Authenticate: Basic。原文为了绕过证书问题使用 curl -Ik https://127.0.0.1:9100/metrics;这是一次 HEAD 请求,而且关闭证书验证。更完整的验收应在正确 CA、SAN 与目标地址下分别检查无凭据、错误凭据和正确凭据的 GET 请求。这里仅描述检查意图,不宣称已完成。
此时 Prometheus 的旧配置没有 Basic Auth 信息,因此抓取失败。原文第二张截图正是启用认证后返回 401 的状态;这不是最终恢复成功的截图。
第六步:为抓取端补上凭据
在前面的 node_exporter job 中增加 basic_auth。下面保留原文完整结构和公开示例密码,以便明确它与服务端哈希的关系;部署时必须重新生成独立口令,不能复制公开值。
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node_exporter'
scheme: https
tls_config:
ca_file: node_exporter.crt
basic_auth:
username: prometheus
password: moelove.info # 公开示例,禁止用于真实服务
static_configs:
- targets: ['localhost:9100']
服务端存 bcrypt 哈希,抓取端需要能用于认证的原始秘密;把同一个 bcrypt 哈希填进客户端密码字段不会代替原始密码。应按目标 Prometheus 版本支持的秘密文件或凭据管理方式部署,限制配置、备份和日志的读取权限,不把真实密码提交到代码仓库。
原文通过 killall -HUP prometheus 重新加载配置。这个命令会给所有匹配名称的进程发信号,可能误影响同机多个实例;实际操作应先核对配置,再使用目标服务管理器或明确实例的 PID 进行重载。本文未向任何进程发送信号。作者文字记录了重载后抓取恢复,但没有给出对应的新截图或本次可复用的测试结果。
把历史案例用于今天
这套案例的价值是分层排错:先确认服务使用 HTTPS,再确认 CA 和主机名称,随后看认证失败是否符合预期,最后验证抓取端是否使用正确凭据。原作者也强调,生产部署应规范 CA 与密码管理,且 Basic Auth 支持多个用户名。文末关于其他 Prometheus 组件将陆续支持这些能力的说法,是 2020 年的展望,不能替代对各组件当前版本的核验。
本次没有启动 exporter 或 Prometheus、生成真实凭据、访问生产指标端口或执行重载。静态审核发现的主要风险为旧参数、CN-only 证书、无加密私钥、跳过证书验证、公开示例口令与过宽信号目标;未发现其他问题不等于无漏洞。图片为本稿原创流程图,未冒充原作者的两个截图。原作者权利与来源归属继续保留。












暂无评论内容