FEATURED · 精选文章

Prometheus监控系统安全加固实战:从配置加密到统一认证

发布时间 / 2026/8/17 10:51:47
来源 / 创域科博编辑部
栏目 / 资讯中心
Prometheus监控系统安全加固实战:从配置加密到统一认证 1. 从“裸奔”到“上锁”为什么你的监控系统也需要安全门禁最近在帮一个朋友的公司做内部监控系统的安全审计发现一个挺普遍但容易被忽视的问题他们的Prometheus监控服务直接暴露在内部网络上没有任何访问控制。用他的话说“反正都是内网而且Prometheus本身也没提供登录界面就这么用着呗”。这个想法其实挺危险的。想象一下你家里的保险柜虽然放在卧室里但连个密码锁都没有任何能进你卧室的人都能随意翻看里面的东西。Prometheus里存放着服务器性能、业务指标、甚至是错误日志的聚合信息这些数据一旦泄露或被恶意篡改轻则暴露系统架构细节重则可能被用来发起精准攻击。Prometheus本身的设计哲学是“拉取Pull模型”和“无状态”这带来了部署的简洁性但也意味着它原生缺乏像用户名密码登录这样的“前端”认证机制。它更像一个专注的数据采集和存储引擎安全边界需要我们自己来构建。这就引出了我们常说的两种主流加固思路一是对Prometheus自身的配置进行加密和访问控制二是在其前方架设一个反向代理如Nginx、Apache或API网关来实现统一的登录认证。网上关于“Prometheus Grafana安装部署”、“k8s部署prometheus监控”的教程一抓一大把但往往到了安全配置这一步就一笔带过或者只给个最简单的basic_auth例子。实际落地时你会遇到一堆坑配置文件加密后服务起不来、TLS证书配置不对、反向代理的认证信息没正确传递给Prometheus导致一直401、在K3s这种轻量K8s环境里配置更是一团乱麻。这篇文章我就结合最近几次的实战和踩坑经历把Prometheus的加密配置和两种登录认证方式内置基础认证 与 反向代理集成认证的完整流程、核心原理以及那些教程里不会写的“坑点”给你讲透。2. 基石理解Prometheus的安全模型与配置加密在动手加锁之前得先搞清楚门Prometheus本身的结构。Prometheus的安全不是零散的补丁而是一个从传输、配置到访问的层次化模型。很多人直接跳到“如何加登录”却忽略了更基础的环节。2.1 Prometheus的“安全三明治”传输层、配置层与应用层Prometheus的安全考量可以粗略分为三层传输层安全TLS确保Prometheus与抓取目标Targets、与Alertmanager、以及与查询客户端如Grafana、PromQL界面之间的通信是加密的防止网络嗅探。这是通过HTTPS和TLS证书实现的。配置层安全保护prometheus.yml这个核心配置文件。里面可能包含访问其他服务的凭证如basic_auth密码、云厂商的密钥等敏感信息。让这些信息以明文形式躺在磁盘上是极不安全的。应用层访问控制即“登录认证”控制谁有权限访问Prometheus的HTTP API包括Web UI、/api/v1/query等端点。这是本文的重点。很多初学者会混淆后两者。配置加密是为了保护“Prometheus如何连接别人”的秘密登录认证是为了保护“别人如何连接Prometheus”的入口。两者相辅相成。2.2 实战使用SOPS与Age加密敏感配置假设你的prometheus.yml里需要配置抓取一个需要认证的Exporter通常你会这样写scrape_configs: - job_name: node_exporter static_configs: - targets: [node01:9100] basic_auth: username: monitor_user password: SuperSecretPassword123! # 明文密码让这个密码明文存储是灾难。我们的目标是让它加密。这里我推荐SOPSSecrets OPerationS Age的组合。SOPS是一个支持多种加密后端的文件编辑器Age是一个现代、简单的加密工具。相比传统的GPGAge更易于管理和自动化。步骤一安装与生成密钥# 安装SOPS和Age # 以macOS为例 brew install sops age # 生成Age密钥对 age-keygen -o age-key.txt这会生成两个文件age-key.txt你的私钥务必妥善保管和公钥在终端输出中以age1...开头。公钥可以放心地交给需要加密文件的同事或放入CI/CD系统。步骤二创建加密规则文件.sops.yaml在Prometheus配置目录下创建这个文件告诉SOPS如何加密creation_rules: - path_regex: .*\.(yml|yaml)$ age: age1yourpublickeyhere # 替换成你的Age公钥 encrypted_regex: ^(password|secret|key|token|credential)$这个规则表示对所有YAML文件使用指定的Age公钥加密并且自动加密匹配encrypted_regex正则表达式的字段值。步骤三加密配置文件cd /your/prometheus/config/dir sops --encrypt --in-place prometheus.yml执行后你的prometheus.yml会变成类似这样scrape_configs: - job_name: node_exporter static_configs: - targets: [node01:9100] basic_auth: username: ENC[AES256_GCM,data:...] password: ENC[AES256_GCM,data:...] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: - recipient: age1yourpublickeyhere enc: | -----BEGIN AGE ENCRYPTED FILE----- ... -----END AGE ENCRYPTED FILE----- ...敏感字段被加密了文件末尾添加了SOPS的元数据。步骤四让Prometheus读取加密文件Prometheus不能直接读加密文件。你需要一个“解密-加载”的过程。有两种主流方式方式A在启动前解密适合传统部署。使用一个启动脚本或systemd服务单元在启动Prometheus前用SOPS解密到一个临时位置然后启动。#!/bin/bash sops --decrypt prometheus.enc.yml prometheus.dec.yml /usr/local/bin/prometheus --config.fileprometheus.dec.yml方式B使用SOPS作为配置管理工具适合K8s/GitOps。在CI/CD流水线中用SOPS解密文件然后将解密后的内容作为ConfigMap或Secret应用到K8s集群中。这是更云原生的做法秘密根本不落地到节点磁盘。踩坑点1文件权限与密钥管理。你的私钥age-key.txt必须严格限制权限如chmod 600。在K8s中可以将私钥存储在Secret中通过Volume挂载给初始化容器用于解密。切勿将私钥提交到Git仓库。踩坑点2加密字段的识别。SOPS的encrypted_regex规则需要仔细设计。像password这样的字段好说但有些Exporter可能用secret_key、auth_token等字段名。你需要根据实际使用的Exporter文档来调整正则表达式确保所有敏感信息都被覆盖。一个更激进但安全的做法是加密整个basic_auth或authorization块。通过配置加密我们解决了“秘密存储”的问题。接下来我们解决“大门看守”的问题。3. 方式一启用Prometheus内置的HTTP基础认证这是最直接的方式利用Prometheus Web框架支持的web.config.yml文件。它允许你为Prometheus的HTTP服务配置TLS和基础认证。3.1 配置生成与部署全流程首先你需要创建一个web.config.yml文件。这个文件与prometheus.yml同级。tls_server_config: cert_file: /path/to/your/cert.pem key_file: /path/to/your/key.pem basic_auth_users: prometheus_admin: $2y$12$4S5nP6M8qCbBzFpKjLdOe.YourHashedPasswordStringHeretls_server_config: 可选但强烈推荐。启用HTTPS。你需要准备或生成自签名/CA签名的证书。basic_auth_users: 定义用户名和bcrypt哈希后的密码。生成bcrypt哈希密码需要安装apache2-utils或使用Go的bcrypt库# 使用htpasswd htpasswd -nBC 12 prometheus_admin # 然后输入密码会输出类似 prometheus_admin:$2y$12$... 的字符串复制$2y$12$...部分即可。 # 或者使用Go (如果你有Go环境) go run $(go env GOPATH)/src/golang.org/x/crypto/bcrypt/generate.go -prompt然后修改Prometheus启动命令指定这个web配置文件./prometheus \ --config.fileprometheus.yml \ --web.config.fileweb.config.yml \ --web.listen-address:9090现在访问https://your-prometheus:9090或HTTP如果你没配TLS浏览器就会弹出登录框了。3.2 深坑预警TLS、哈希与路径陷阱这种方式听起来简单但坑点密集。坑点一自签名证书的信任问题。如果你用的是自签名证书浏览器和Grafana都会报错“不安全的连接”。对于浏览器你可以手动添加例外不推荐生产环境。对于Grafana在配置Prometheus数据源时必须在Access模式选择Server (default)并在Auth部分勾选Skip TLS Verify跳过TLS验证。但这只是绕过了验证并没有加密。生产环境应使用受信任的CA签发的证书。坑点二密码哈希的代价与强度。bcrypt的cost参数例子中的12决定了哈希的计算强度和时间。12是一个在2023年仍被认为安全的强度但意味着每次验证登录都需要一定的CPU时间。这能有效抵御暴力破解但如果你有非常高频的访问比如大量自动化脚本可能会对Prometheus服务器造成轻微负载。切勿为了性能而降低cost值到10以下。坑点三--web.external-url的副作用。这个参数常被用于配置Prometheus在反向代理或负载均衡器后的外部访问地址。但如果你设置了--web.external-urlhttps://proxy.example.com/prometheus并且同时启用了基础认证那么Prometheus在重定向如访问根路径/时生成的登录后URL可能会出错导致登录循环。解决方案是确保你的反向代理下一节会讲正确传递了Host头和X-Forwarded-*头并且web.config.yml中的认证配置能正确处理这些代理场景。有时可能需要完全依赖反向代理来做认证而关闭Prometheus自身的认证。坑点四对抓取目标Scrape和远程读写无效。非常重要web.config.yml中的认证只保护了Prometheus自身HTTP服务的入站连接即Web UI和API。它并不为Prometheus出去抓取目标Scrape提供认证凭证也不为远程写入Remote Write的接收端提供认证。为目标配置认证仍然需要在prometheus.yml的scrape_configs下的basic_auth或authorization里设置。这是两个完全独立的通道。内置基础认证适合小型、内部环境或者作为一道简单的安全屏障。但对于需要复杂认证如LDAP、OAuth2、统一登录、或者已有网关架构的系统就需要更强大的方式二。4. 方式二通过反向代理实现统一认证与接入这是更灵活、更企业级的方法。核心思想是不让用户直接访问Prometheus而是访问前面的一个反向代理如Nginx、Traefik、Apache HTTPD。由这个代理来处理TLS终止、负载均衡和所有的认证逻辑。Prometheus对此一无所知它只看到来自代理的、已经认证过的请求。4.1 为什么选择反向代理场景与优势分析你可能会问既然Prometheus自己能做基础认证为什么还要多引入一个组件原因在于“解耦”和“扩展性”统一认证门户公司可能已有统一的OAuth2/SSO单点登录系统。你不可能让Prometheus去对接所有的SSO协议。但Nginx可以通过插件如auth_request模块或Lua脚本轻松集成。复杂的访问策略例如你想实现基于IP的访问控制只允许运维网段访问、基于路径的权限/api/v1/query只允许特定用户组访问、或者限流防刷。简化证书管理只需要在反向代理上配置和管理TLS证书后端所有服务Prometheus, Alertmanager, Grafana等都可以走纯HTTP简化了内部通信。聚合端点你可以通过一个域名和端口通过不同的路径如/prometheus,/grafana,/alertmanager暴露整个监控栈用户体验更好。以最常用的Nginx为例我们来搭建一个具备基础认证和TLS的代理。4.2 Nginx反向代理配置详解与避坑假设你的Prometheus运行在localhost:9090你想通过https://monitor.yourcompany.com/prometheus来访问。第一步生成密码文件与Prometheus内置认证类似sudo mkdir -p /etc/nginx/.secrets sudo htpasswd -c /etc/nginx/.secrets/htpasswd.prometheus prometheus_user # 输入密码文件会被创建。第二步配置Nginx Server Blockserver { listen 443 ssl http2; server_name monitor.yourcompany.com; # TLS证书配置 ssl_certificate /etc/ssl/certs/your-cert.pem; ssl_certificate_key /etc/ssl/private/your-key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # 基础认证 auth_basic Prometheus Monitoring; auth_basic_user_file /etc/nginx/.secrets/htpasswd.prometheus; # 代理Prometheus location /prometheus/ { # 关键移除传递给后端请求的认证头避免Prometheus也要求认证 proxy_set_header Authorization ; # 标准代理头 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 重写URL将 /prometheus/ 前缀去掉再传递给后端 rewrite ^/prometheus/(.*) /$1 break; # 代理到Prometheus proxy_pass http://localhost:9090/; # 如果Prometheus设置了--web.external-url这里需要匹配 # proxy_pass http://localhost:9090/prometheus/; } # 可选同时代理Grafana location /grafana/ { proxy_pass http://localhost:3000/; # ... 其他代理设置 } }第三步调整Prometheus的--web.external-url为了让Prometheus生成的链接如转到Graph页面的链接正确指向代理地址你需要启动Prometheus时加上--web.external-urlhttps://monitor.yourcompany.com/prometheus4.3 高级集成对接OAuth2与单点登录SSO基础认证只是开始。真正的企业级需求是集成公司现有的单点登录。这里以Nginx的ngx_http_auth_request_module模块为例搭配一个简单的OAuth2代理服务如oauth2-proxy来说明原理。架构流程如下用户访问https://monitor.yourcompany.com/prometheus。Nginx的auth_request指令将请求转发给oauth2-proxy运行在本地某个端口进行认证检查。oauth2-proxy检查用户会话Cookie。如果无效则将用户重定向到公司的OAuth2提供商如Google, GitHub, 泛微OA 用友NCC等进行登录。用户在OAuth2提供商处登录成功后被重定向回oauth2-proxyoauth2-proxy设置一个加密的会话Cookie。oauth2-proxy返回200 OK给Nginx表示认证通过。Nginx将原始请求代理给后端的Prometheus。关键的Nginx配置片段location /prometheus/ { # 启用认证请求 auth_request /oauth2/auth; # 认证失败时的错误页面 error_page 401 /oauth2/sign_in; # 将必要的头传递给OAuth2代理和后端 auth_request_set $user $upstream_http_x_auth_request_user; proxy_set_header X-Forwarded-User $user; # 将认证请求代理给oauth2-proxy location /oauth2/auth { internal; proxy_pass http://127.0.0.1:4180; # oauth2-proxy端口 proxy_pass_request_body off; proxy_set_header Content-Length ; proxy_set_header X-Original-URI $request_uri; } # 登录入口 location /oauth2/ { proxy_pass http://127.0.0.1:4180; } # 实际代理到Prometheus proxy_pass http://localhost:9090/; rewrite ^/prometheus/(.*) /$1 break; }oauth2-proxy需要单独部署和配置指定你的OAuth2客户端ID、密钥、回调URL等。这样你就实现了基于公司统一账号的Prometheus访问控制。踩坑点路径重写与静态资源。这是反向代理配置中最常见的坑。Prometheus的Web UI会加载JavaScript、CSS等静态资源这些资源的路径可能是绝对的。如果你的代理路径/prometheus/和后端服务的根路径/不匹配会导致页面样式丢失、API调用404。务必确保rewrite规则正确并且Prometheus的--web.external-url设置与代理的公共访问路径完全一致。浏览器的开发者工具F12的“网络Network”选项卡是你排查这类问题的利器查看失败的资源请求URL是什么再回头调整你的rewrite或proxy_pass规则。5. 在KubernetesK8s/K3s环境中的特殊考量在容器化、动态的K8s环境中部署Prometheus安全配置的玩法又不一样了。无论是使用Prometheus Operator还是原生部署核心思想都是利用K8s的原生资源。5.1 使用Secret管理敏感配置在K8s里绝对不要把密码写在ConfigMap里。对于prometheus.yml中的basic_auth密码或者web.config.yml中的TLS密钥都应该使用Secret。创建包含密码的Secretkubectl create secret generic prometheus-scrape-secrets \ --from-literalusernamemonitor_user \ --from-literalpasswordSuperSecretPassword123! \ -n monitoring在Prometheus的Deployment或StatefulSet中挂载Secret并在prometheus.yml中引用# prometheus.yml 片段 scrape_configs: - job_name: secured_exporter static_configs: - targets: [secure-exporter:8080] basic_auth: username: $(SCRAPE_USERNAME) password: $(SCRAPE_PASSWORD)# Deployment spec片段 spec: containers: - name: prometheus image: prom/prometheus:latest args: - --config.file/etc/prometheus/config/prometheus.yml env: - name: SCRAPE_USERNAME valueFrom: secretKeyRef: name: prometheus-scrape-secrets key: username - name: SCRAPE_PASSWORD valueFrom: secretKeyRef: name: prometheus-scrape-secrets key: password volumeMounts: - name: config-volume mountPath: /etc/prometheus/config volumes: - name: config-volume configMap: name: prometheus-config注意这里用了环境变量替换。Prometheus支持在配置文件中使用$(VAR_NAME)语法引用环境变量。更复杂的配置可以直接将整个web.config.yml内容创建为一个Secret然后挂载为文件。5.2 Ingress集成认证以Nginx Ingress Controller为例在K8s中通常使用Ingress来暴露服务而不是直接使用Service的NodePort或LoadBalancer。Nginx Ingress Controller本身就是一个强大的反向代理可以直接配置认证。为Prometheus创建Ingress并启用基础认证创建一个包含用户名密码的Secret注意格式必须是auth文件格式htpasswd生成。htpasswd -c auth prometheus_user kubectl create secret generic prometheus-basic-auth --from-fileauth -n monitoring在Ingress注解中启用认证apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: prometheus-ingress namespace: monitoring annotations: nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: prometheus-basic-auth nginx.ingress.kubernetes.io/auth-realm: Authentication Required - Prometheus spec: ingressClassName: nginx rules: - host: prometheus.yourdomain.com http: paths: - path: / pathType: Prefix backend: service: name: prometheus-service port: number: 9090这样访问prometheus.yourdomain.com时就会弹出基础认证框。认证由Ingress Controller处理后端的Prometheus Service无需任何认证配置。对于OAuth2你可以部署oauth2-proxy作为一个独立的Deployment和Service然后在Ingress的注解中配置nginx.ingress.kubernetes.io/auth-url指向oauth2-proxy的认证端点原理与上一节Nginx配置类似。5.3 K3s轻量环境的注意事项K3s默认使用Traefik作为Ingress Controller。配置方式与Nginx Ingress有所不同。你需要使用Traefik的Middleware来添加认证。创建基础认证的Secret同样需要htpasswd格式kubectl create secret generic prometheus-auth-secret --from-fileusers/path/to/auth -n monitoring创建Traefik的Middleware资源apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: prometheus-auth namespace: monitoring spec: basicAuth: secret: prometheus-auth-secret在IngressRouteTraefik的CRD中引用这个MiddlewareapiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: prometheus namespace: monitoring spec: entryPoints: - websecure routes: - match: Host(prometheus.yourdomain.com) kind: Rule services: - name: prometheus-service port: 9090 middlewares: - name: prometheus-auth tls: secretName: your-tls-secret踩坑点K8s Secret的编码与更新。K8s Secret的data字段要求值是base64编码的。使用kubectl create secret generic --from-literal或--from-file时kubectl会自动帮你编码。但如果你需要更新Secret直接kubectl edit然后修改base64后的字符串很容易出错。建议使用kubectl create secret ... --dry-runclient -o yaml生成模板或者使用像SealedSecrets、External Secrets这样的工具来管理。另外更新Secret后引用了该Secret的Pod需要重启才能加载新的值除非你使用了一些支持热加载的边车模式。6. 认证生效后的连锁反应Grafana、Alertmanager与远程读写当你给Prometheus加上了认证所有依赖它的组件都需要相应调整否则整个监控链路会断裂。6.1 配置Grafana数据源这是最直接的受影响点。在Grafana中添加或修改Prometheus数据源时在URL字段填写你的Prometheus地址如果是通过反向代理填代理地址。在Auth部分如果Prometheus启用了基础认证勾选Basic auth并填写对应的User和Password。如果使用了反向代理且代理层做了认证Grafana数据源这里不需要配置认证因为Grafana到反向代理的请求认证已经由代理处理了比如通过IP白名单允许Grafana服务器IP直接访问。但更安全的做法是在反向代理上为Grafana设置一个专用的、带有认证头的内部访问路径。如果启用了TLSHTTPS根据证书情况可能需要在TLS/SSL Auth下配置CA cert或勾选Skip TLS Verify仅用于自签名证书测试。点击Save TestGrafana会尝试连接Prometheus。如果成功会显示“Data source is working”。6.2 配置AlertmanagerPrometheus需要将警报推送给Alertmanager。如果Alertmanager也配置了认证同样通过--web.config.file或反向代理你需要在prometheus.yml的alerting部分配置对应的认证信息。alerting: alertmanagers: - static_configs: - targets: [alertmanager:9093] # 如果Alertmanager需要基础认证 basic_auth: username: alertmanager_user password: $(ALERTMANAGER_PASSWORD) # 同样建议从Secret读取 # 或者如果使用Bearer Token # authorization: # credentials: $(ALERTMANAGER_TOKEN) # 如果使用TLS # scheme: https # tls_config: # ca_file: /path/to/ca.pem # insecure_skip_verify: false6.3 远程读写Remote Read/Write端点如果你使用了远程存储如Thanos、Cortex、M3DB或者有从其他Prometheus实例远程写入的需求这些远程端点如果开启了认证也需要在prometheus.yml的remote_write或remote_read配置块中进行相应设置支持basic_auth、authorization、oauth2等多种方式。一个常见的连环坑你为Prometheus配置了反向代理认证然后Grafana数据源配置了该代理地址和认证信息。一切正常。后来你又在Prometheus前面加了一个WAFWeb应用防火墙或全局负载均衡器它修改了HTTP头或导致了重定向。突然Grafana测试数据源失败但浏览器直接访问却正常。这时候你需要检查从Grafana服务器发出的实际请求可以在Grafana容器内用curl -v调试查看请求头、响应码和重定向链问题往往出在多层代理之间的头信息传递或TLS证书上。安全从来不是一蹴而就的而是一个持续的过程。从明文配置到加密存储从任意访问到严格认证每一步都在提升系统的防御纵深。对于Prometheus这样核心的监控组件投入时间做好安全配置绝不是过度设计而是运维责任的基本体现。我个人的习惯是在任何内部系统部署的Checklist上“认证与授权”永远是排在前三项的必选项。毕竟安全上的“侥幸心理”往往是未来某个深夜告警电话的根源。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻