前面把监控体系从「能采集」一路做到了「能看、能告警、能出报告、还能高可用长期存」。但是:系统本身几乎是敞开的。
本篇目标:覆盖 Prometheus / Exporter / Alertmanager / Grafana / 网络五个环节的加固做法,重点解决三件事:
- 把默认裸奔的接口锁上认证
- 把「谁能看什么」做成分级授权
- 把敏感数据挡在采集与展示之外
原则只有一条:监控网络也不可信,默认拒绝,按需放行。
一、先给监控系统定个性:它是高危资产
1、监控系统握着什么
| 资产 | 攻击者拿到后能做什么 |
|---|---|
| 全量指标数据 | 画出资产拓扑、依赖关系、容量水位 → 精准侦察 |
| 主机/应用明细指标 | node_exporter 的 env、启动参数、挂载点可能直接泄露路径与凭据 |
| 主动抓取能力 | Prometheus 能访问所有 target,等于一个内网探测器,可被当跳板 |
| 数据删除能力 | admin API 可删序列、清墓碑,破坏事后取证 |
| 告警通道 | 伪造/轰炸告警,做钓鱼或淹没真告警 |
| Grafana 面板 | 面板是数据出口,插件与数据源配置甚至可能成为命令执行入口 |
2、默认配置的风险清单
先把「出厂即不安全」的地方列清楚,再逐条处理:
| 组件 | 默认状态 | 风险等级 |
|---|---|---|
| Prometheus Web / HTTP API | 无认证、无 TLS | 高 |
| Prometheus admin API | 默认关闭,但第12篇为做快照开过 | 一旦开启即高危 |
| node_exporter (:9100) | 无认证、监听 0.0.0.0 | 高 |
| Alertmanager (:9093) | 无认证 | 中 |
| Grafana | admin/admin,匿名访问可开 | 高 |
| Docker 端口映射 | -p 9090:9090 默认绑全网卡 |
高 |
二、Prometheus:把 Web 与 API 锁上
1、Web 与 API 认证(web.config.file)
Prometheus 从 v2.24 起支持 --web.config.file,用一份独立配置同时开 TLS 和 basic auth,是最省事的加固手段。
先生成密码哈希:
bash
# 用 htpasswd 生成 bcrypt 哈希(-B 强制 bcrypt,-C 10 代价因子)
$ htpasswd -nBC 10 "" | tr -d ':'
$2y$10$.......... # 复制这一串
写 web-config.yml:
yaml
# /etc/prometheus/web-config.yml
tls_server_config:
cert_file: /etc/prometheus/certs/prometheus.crt
key_file: /etc/prometheus/certs/prometheus.key
min_version: TLS12 # 拒绝 TLS1.0/1.1
basic_auth_users:
monitor: $2y$10$.......... # 认证用户 monitor
readonly: $2y$10$.......... # 只读账号
启动参数挂上:
bash
--web.config.file=/etc/prometheus/web-config.yml
⚠️ 注意:web-config.yml 里的密码是 bcrypt 哈希而非明文,但文件本身要 chmod 600,且不要和 prometheus.yml 混在同一个只读挂载里。
2、别让 admin API 常开
--web.enable-admin-api 一旦打开,就多了以下能改数据的接口:
| 接口 | 作用 |
|---|---|
/api/v1/admin/tsdb/delete_series |
删除匹配的序列 |
/api/v1/admin/tsdb/clean_tombstones |
清墓碑,删除不可恢复 |
/api/v1/admin/tsdb/snapshot |
生成快照 |
3、抓取端认证:让 Prometheus 能过 target 的鉴权
加固不能只加固 Prometheus 自己,当 exporter 或业务端点也要求认证时,scrape 配置得能带凭据。三种方式:
yaml
scrape_configs:
# 方式一:basic_auth
- job_name: "node-detail"
basic_auth:
username: monitor
password_file: /etc/prometheus/secrets/node_pw # 用 *_file,别写明文
# 方式二:Bearer Token(推荐用 authorization)
- job_name: "spring-app"
authorization:
type: Bearer
credentials_file: /etc/prometheus/secrets/app_token
# 方式三:TLS 客户端证书(mTLS)
- job_name: "secure-target"
scheme: https
tls_config:
ca_file: /etc/prometheus/certs/ca.crt
cert_file: /etc/prometheus/certs/client.crt
key_file: /etc/prometheus/certs/client.key
insecure_skip_verify: false # 绝不要设 true
⚠️ 注意:优先用 _file 变体(password_file / credentials_file)。 明文写在 prometheus.yml 里的凭据,会随配置进 Git、进备份、进镜像层,属于典型的「写死即泄露」。
4、远程读写(remote_write)走加密通道
第12篇的 remote_write 默认是明文 HTTP。跨机房或跨网段时,改成 HTTPS + 认证:
yaml
remote_write:
- url: https://victoria-metrics:8428/api/v1/write
basic_auth:
username: writer
password_file: /etc/prometheus/secrets/rw_pw
tls_config:
ca_file: /etc/prometheus/certs/ca.crt
5、收敛危险端点
除 admin API 外,还有几个端点要留意:
| 端点 | 说明 | 处理 |
|---|---|---|
/-/reload |
配置热加载,需 --web.enable-lifecycle |
反代层限制来源 |
/debug/pprof/* |
性能剖析,可能泄露内存中的敏感信息 | 生产环境在反代层屏蔽 |
/api/v1/query |
可执行任意 PromQL | 必须跟 Web 一样认证 |
第一道也是最有效的一道防线:反代层只放行白名单路径。
三、Exporter:管好「口子」和「内容」
1、监听地址:从 0.0.0.0 收到内网
exporter 是最容易被忽略的暴露面,exporter 通常以 root 或高权限运行,且监听全网卡。
bash
# node_exporter 只监听本机,由 Prometheus 走同宿主机或隧道访问
node_exporter --web.listen-address=127.0.0.1:9100
# 若 Prometheus 在另一台机器,则绑定内网网卡
node_exporter --web.listen-address=10.0.2.15:9100
再叠加防火墙/安全组只放行 Prometheus 的 IP:
bash
# 只允许 Prometheus 主机访问 9100
$ iptables -A INPUT -p tcp --dport 9100 -s 10.0.2.10 -j ACCEPT
$ iptables -A INPUT -p tcp --dport 9100 -j DROP
2、exporter 自身也开认证
node_exporter 同样支持 --web.config.file:
bash
node_exporter \
--web.listen-address=:9100 \
--web.config.file=/etc/node_exporter/web-config.yml
配合第二节 scrape 的 basic_auth,端点就从「裸奔」变成「持证访问」。
3、挡住敏感指标:先看清「默认开没开」
很多人以为 exporter 会泄露一堆敏感指标,其实恰恰相反,node_exporter 里很多高危的 collector 默认就是关闭的,风险反而常来自「为排障临时打开、用完忘了关」。所以加固第一步不是裁剪,而是先核对到底开了哪些 collector:
bash
# 查看当前各 collector 的启用与采集情况
$ curl -s http://localhost:9100/metrics | grep '^node_scrape_collector_success'
| collector / 指标 | 默认状态 | 泄露内容 | 建议 |
|---|---|---|---|
systemd(node_systemd_unit_*) |
默认关闭 | 单元名、运行状态,可能含业务命名 | 仅排障时临时 --collector.systemd;长期开启需评估 |
processes(node_processes_*) |
默认关闭 | 进程数、可按状态聚合 | 需要才开,非必要不开 |
logind / interrupts / perf |
默认关闭 | 登录会话、中断、性能剖析 | 同上,非必要不开 |
node_filesystem_*(mountpoint 标签) |
默认开启 | 挂载点路径,可能暴露业务目录结构 | 视情况裁剪 |
node_uname_info |
默认开启 | 内核版本、主机名 | 接受或裁剪 |
node_network_info |
默认开启 | 网卡 MAC、operstate | 一般保留 |
⚠️ 注意:「默认关闭」不等于安全。 一旦有人加 --collector.systemd 把 systemd collector 打开,node_systemd_unit_* 这类敏感指标立刻上线;而这类「临时开启」往往没人记录、没人回收。真正要做的是先摸清并锁定 collector 清单,把它纳入变更与巡检。
裁剪仍在采集末端做(metric_relabel_configs),但作用对象应是你确认开启、且确属敏感的序列:
yaml
metric_relabel_configs:
# 例:若确实开了 systemd collector,又不想让单元名入库
- source_labels: [__name__]
regex: "node_systemd_unit_state"
action: drop
⚠️ 注意:drop 不可逆,历史和未来一起消失,先在线下验证无人引用再上生产。
4、Pushgateway 与短命任务
短命任务走 Pushgateway,默认也不鉴权,且任何人 push 的指标都会长期留存。加固要点:
bash
pushgateway --web.config.file=/etc/pushgateway/web-config.yml \
--web.listen-address=127.0.0.1:9091
并约定 push 时带 job/instance 命名规范,便于审计来源。
四、Alertmanager 与通知链路
1、Alertmanager 的认证
Alertmanager 从 v0.22 起也支持 --web.config.file:
bash
alertmanager \
--config.file=/etc/alertmanager/alertmanager.yml \
--web.config.file=/etc/alertmanager/web-config.yml \
--cluster.listen-address=0.0.0.0:9093
集群里,节点间 gossip 通信(cluster 端口)同样应限制在集群内部网段,不要暴露。
2、通知渠道的凭据别明文
alertmanager.yml 里常出现 webhook 密钥、邮箱密码、IM token。以下反例是重灾区:
yaml
# ❌ 反例:明文写在配置里
receivers:
- name: "webhook"
webhook_configs:
- url: "https://hook.example.com/alert?token=abc123"
改用外部文件引用:
yaml
# ✅ 正确:从外部文件读取
receivers:
- name: "webhook"
webhook_configs:
- url_file: /etc/alertmanager/secrets/webhook_url
3、告警内容脱敏
第9篇的告警模板会把 label 渲染进通知,而 label 里可能带 instance(暴露内网 IP)、svc、甚至业务标识。发往企业微信、钉钉、外部 Webhook 前,检查模板是否泄露了不该外传的信息。涉及外部渠道时,宁可只发 summary + description,不发原始 label 全集。
五、Grafana:权限治理的主战场
「权限治理」落到实践上大半都在 Grafana。因为 Grafana 直接面向人:开发、测试、运维、业务方、负责人,各自该看到什么、能改什么,全靠这一层约束。
1、上线第一件事:关掉匿名与默认口令
ini
# grafana.ini
[auth.anonymous]
enabled = false # 禁止匿名访问
[security]
admin_user = admin
admin_password = <强口令>
cookie_secure = true # 仅 HTTPS 传输 cookie
[users]
allow_sign_up = false # 禁止自助注册
⚠️ 注意:admin/admin 是 Grafana 最经典的失守点。装完先改口令,再谈其他。
2、认证集成:从本地账号到 SSO
不要在 Grafana 里单独建号、单独发密码。应对接企业统一认证,账号生命周期跟着 HR/AD 走:
| 方式 | 适用 | 关键配置 |
|---|---|---|
| LDAP / AD | 已有域控 | [auth.ldap],配 bind 与 group 映射 |
| OAuth / OIDC | 已有 SSO(Keycloak、企业微信、飞书等) | [auth.generic_oauth] |
| SAML | 企业级 SSO | [auth.saml](需企业版) |
OIDC 核心片段:
ini
[auth.generic_oauth]
enabled = true
name = SSO
client_id = grafana
client_secret = ${GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET} # 走环境变量,不写死
scopes = openid profile email groups
auth_url = https://sso.example.com/auth
token_url = https://sso.example.com/token
api_url = https://sso.example.com/userinfo
role_attribute_path = contains(groups[*], 'monitor-admins') && 'Admin' || 'Viewer'
最后一行:用 SSO 的 group 自动决定 Grafana 角色,不用在 Grafana 里手工授权。
3、三层模型:组织、团队、用户
Grafana 的权限是分层叠加的,先理清三个概念:
| 概念 | 含义 | 用途 |
|---|---|---|
| Organization(组织) | 彼此隔离的空间,数据源与 Dashboard 不互通 | 多租户,如「生产」与「研发测试」 |
| Team(团队) | 组织内的一组用户 | 按职责分组,如「支付组」「基础架构组」 |
| User(用户) | 账号 | 最小授权单位 |
4、角色:给最小权限
Grafana 内置三种组织角色(OSS 版):
| 角色 | 能做什么 | 给谁 |
|---|---|---|
| Viewer | 只看,不能改 | 业务方、上级 |
| Editor | 能建改 Dashboard,但不能动数据源/用户 | 开发、测试 |
| Admin | 能管数据源、用户、组织设置 | 运维、平台管理员 |
⚠️ 注意:默认只给 Viewer,不能给 Editor。 Editor 虽不能管用户,但能创建任意查询、导出数据、改面板,等同于能拿到他能访问的所有数据。业务方一律 Viewer 起步。
5、文件夹权限:按团队隔离
Dashboard 靠文件夹(Folder)组织,权限也挂在文件夹上:
yaml
组织: 生产监控
├── 文件夹: 支付业务面板 → Team: 支付组 (Edit)、业务方 (View)
├── 文件夹: 基础架构面板 → Team: 基础架构组 (Edit)
└── 文件夹: 容量与成本 → 仅 Admin 可见
在 Folder → Permissions 里配置:View / Edit / Admin 三级,分别授权给 User / Team / Role。默认继承组织的「Viewer 可见」,要隔离就把默认降权,再显式授权。
6、数据源权限:最容易被忽略的盲区
⚠️ 默认情况下,任何登录用户(包括 Viewer)都能用「Explore」直接对数据源跑任意 PromQL。 即使把 Dashboard 锁得再死,Viewer 只要会写 PromQL,就能把整个 Prometheus 的数据查个底朝天,包括没给他看的面板背后的数据。
解法是 Data source permissions(数据源权限):
r
数据源 prometheus-prod → Permissions → 只授权给 Team: 基础架构组
开启后,只有被显式授权的团队能用这个数据源查询;其他人即使有 Explore 权限,也选不到这个数据源。这就是「面板级隔离」和「数据级隔离」的关键差别。
7、其他几处开关
| 配置 | 作用 | 建议 |
|---|---|---|
viewers_can_edit |
Viewer 是否可临时改面板 | false |
[explore] enabled |
Explore 查询功能 | 按需,敏感环境可关 |
editors_can_admin |
Editor 能否管理其团队的权限 | 谨慎开启 |
[snapshots] external_enabled |
是否允许公开分享快照 | false,快照会绕过权限 |
8、审计
- Grafana 审计日志(Audit Log)记录登录、查询、修改操作,属企业版能力;OSS 版可退而用反向代理访问日志 + SSO 侧日志拼出操作轨迹。
- Prometheus、Alertmanager 的访问也应经反代记录来源 IP,便于事后追查。
六、网络与传输安全
1、网络分段:监控网独立
把 Prometheus、Alertmanager、Grafana、各 exporter 放进独立的监控网段,与业务网、办公网隔离:
- 监控网 → 业务网:只放行抓取所需端口(9100、应用 actuator 等),其余全拒;
- 办公网 → 监控网:只经 Grafana 反代入口,直连 Prometheus/AM 一律拒绝;
- 监控网 → 互联网:原则上不出,如需推送告警,走白名单出站。
2、统一入口:反向代理 + 认证
不能直连 Prometheus 的 9090,用 Nginx 做统一入口,把认证、TLS、路径白名单一次解决:
nginx
server {
listen 443 ssl;
server_name prom.example.com;
ssl_certificate /etc/nginx/certs/prom.crt;
ssl_certificate_key /etc/nginx/certs/prom.key;
# 屏蔽危险端点
location ~ ^/(api/v1/admin|debug/pprof) { return 403; }
location / {
auth_basic "monitor";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://prometheus:9090;
proxy_set_header X-Real-IP $remote_addr; # 记录真实来源,供审计
}
}
Grafana 则让它自己做认证与授权,反代只负责 TLS 与限源:
nginx
location /grafana/ {
allow 10.0.0.0/8; # 仅办公网/内网
deny all;
proxy_pass http://grafana:3000/;
proxy_set_header X-Real-IP $remote_addr;
}
3、mTLS(高安全场景)
对跨机房、跨云的监控链路,用双向 TLS:Prometheus 持有客户端证书,exporter 用 --web.config.file 校验。
4、出站控制
Prometheus 的输出面(remote_write、webhook、抓取)应有出站白名单,防止被攻破后当成数据回传通道。
七、凭据管理:把密钥从配置里拿出来
凭据分三类,处理方式不同:
| 凭据类型 | 示例 | 正确存放 | 反例 |
|---|---|---|---|
| 服务账号密码 | scrape basic_auth | *_file 指向 600 权限文件 |
写在 prometheus.yml |
| API Token | SSO client_secret、webhook token | 环境变量 / K8s Secret / Vault | 写在 grafana.ini、alertmanager.yml |
| 证书私钥 | TLS key | 600 权限,挂载只读 | 打进镜像层 |
文件权限,最小可读原则:
bash
# 凭据文件:只有运行用户可读
$ chmod 600 /etc/prometheus/secrets/node_pw
$ chown 65534:65534 /etc/prometheus/secrets/node_pw # Prometheus 容器内 nobody
# 目录:可进入但不可列举
$ chmod 700 /etc/prometheus/secrets
⚠️ 注意:Docker/K8s 里不要把凭据用 -e PASSWORD=xxx 传。 docker inspect 和进程列表都能看到环境变量明文。用挂载的 secret 文件,或用支持 secret 引用的编排方式(K8s Secret、Compose 的 secrets:)。
八、一份可落地的加固清单
不必一次做完,按「投入产出比」分三批推进即可。
第一批:立杆见效(高风险、低成本)
| 项 | 动作 | 验证 |
|---|---|---|
| 改默认口令 | Grafana 改 admin 密码 | 不再能 admin/admin 登录 |
| 关匿名 | auth.anonymous.enabled=false |
未登录访问被拒 |
| 端口收敛 | Prometheus/Grafana/exporter 绑定内网 IP | 公网扫不到端口 |
| 关 admin API | 移除 --web.enable-admin-api |
/api/v1/admin 404 |
| 凭据文件化 | scrape 密码改 *_file |
配置里搜不到明文 |
第二批:安全升级(需协调)
| 项 | 动作 |
|---|---|
| Web 认证 | Prometheus/AM 配 web.config.file 开 TLS + basic auth |
| 反代收口 | Nginx 统一入口 + 白名单 + 屏蔽 pprof/admin |
| SSO 对接 | Grafana 接 LDAP/OIDC,按 group 映射角色 |
| 数据源权限 | 开启 Data source permissions,按团队授权 |
第三批:常态化(体系化)
| 项 | 动作 |
|---|---|
| 网络分段 | 监控网独立,最小放行 |
| collector 清单 | 锁定启用的 collector,回收临时开启项 |
| 文件夹权限 | 按团队建立文件夹权限矩阵 |
| 凭据管理 | 引入统一 secret 管理(Vault / K8s Secret) |
| 审计 | 开启审计日志,定期 review 高权限账号 |
| 巡检 | 把「是否匿名访问、是否新增 admin API、是否新增非必要 collector、是否有明文凭据」纳入月度巡检 |
九、小结
安全不是监控体系的「附加项」,而是前面十二篇所有能力能否被信任的前提,一个没有认证的 Prometheus,采得越全、看得越透,被攻破时泄露得越彻底。
本篇核心要点:
- 一是默认即不安全。Prometheus、Alertmanager、Grafana、exporter 出厂都是敞开或弱口令的,第一个动作永远是关默认、收端口、上认证。
- 二是认证有三个层面 :入口认证(谁能访问)、抓取认证(Prometheus 怎么证明身份)、传输加密(TLS/mTLS);Prometheus、Alertmanager、node_exporter 都支持同一套
--web.config.file,配置风格统一。 - 三是权限治理的主战场是 Grafana,且要分「面板级」和「数据级」两层,只锁 Dashboard 是假隔离,必须开 Data source permissions,否则 Viewer 用 Explore 就能绕过。
- 四是敏感指标先摸清 collector 再谈裁剪。node_exporter 的高危 collector(如 systemd、processes)默认是关闭的,风险常来自「临时开、忘了关」;加固重点是把 collector 清单管住,而非盲目 drop。
- 五是凭据绝不进配置文件 。用
*_file、环境变量或 secret 管理,配合 600 权限,堵住「配置即泄露」这条最常见的数据外流通道。 - 六是加固可以分期推进:先做「立杆见效」的一批,风险立刻下降一大截,再逐步做到网络分段与审计常态化。
至此,监控体系在能力上和安全上完成了闭环,既采得全、看得清、告得准、存得久,也守得住。