Prometheus Pushgateway未授权访问漏洞修复实战:从原理到Nginx+HTTPS加固
1. 项目概述一次典型的监控组件安全加固那天下午监控告警突然响了不是业务指标异常而是安全扫描平台发来的一条高危告警“Prometheus Pushgateway 组件存在未授权访问风险”。我心里咯噔一下Pushgateway 作为我们监控体系里接收短周期任务指标的关键入口如果真被未授权访问意味着攻击者不仅能窥探到大量内部作业的运行状态、成功率等敏感信息甚至可以通过伪造指标数据来干扰告警误导运维判断。这绝不是小事。Prometheus 生态里Pushgateway 的设计初衷是为了允许那些生命周期短暂、无法被 Prometheus 主动拉取Pull的作业比如批处理脚本、CronJob能够主动推送Push其指标。它像一个临时的指标缓存池。然而正是这种“接收外部数据”的特性如果配置不当极易将其暴露为一个不设防的入口。这次复盘我就把从漏洞确认、分析、修复到最终验证的完整过程以及背后的安全思考详细拆解一遍。无论你是刚开始接触 Prometheus 监控的运维工程师还是负责云原生安全的同学这套从发现问题到闭环验证的思路都应该能给你带来直接的参考。2. 漏洞原理与风险深度剖析2.1 Pushgateway 的“无认证”设计原罪首先得明白Pushgateway 默认就是不设防的。从官方仓库拉取的镜像其默认启动命令和配置并没有包含任何形式的身份验证或授权机制。它监听一个端口默认是9091对外提供两个主要 HTTP 接口/metrics用于 Prometheus 拉取聚合后的指标而/metrics/job/job_name/...这类路径则用于接收指标推送。问题的核心就在这里任何能够访问到 Pushgateway 网络端点IP:Port的客户端都可以无需任何凭证向任意路径推送指标数据也可以随意查询已有的所有指标。这并非一个软件缺陷Bug而是一个默认配置缺失导致的安全弱点。在安全领域我们常说的“未授权访问漏洞”大多属于此类比如早期 Redis、MongoDB 的未授权访问以及 Elasticsearch、Kibana 的类似问题根源都在于服务默认信任了其部署的网络环境。2.2 具体风险场景模拟光说理论不够直观我们来看看如果这个漏洞被利用攻击者具体能做什么信息泄露直接访问http://pushgateway-ip:9091/metrics就能获取所有暂存在 Pushgateway 里的指标。这些指标可能包含批处理作业的名称、实例标识。作业的成功/失败状态my_job_successmy_job_failure。作业执行的耗时、处理的数据量等业务敏感度量。通过标签label携带的额外信息如主机名、环境、业务线等。数据污染与告警欺骗攻击者可以伪造 HTTP POST 请求向 Pushgateway 推送恶意指标。例如将一个关键健康检查作业的成功指标my_critical_job_success推值为 0导致 Prometheus 触发虚假的故障告警引发运维恐慌甚至错误切换。推送一个极高的延迟指标掩盖真实的性能问题。通过推送大量垃圾指标占满 Pushgateway 内存导致其 OOM 崩溃影响正常的监控数据流。作为内网渗透跳板如果 Pushgateway 部署在可与内部其他服务通信的网络区域攻击者或许无法直接利用它执行代码但获取到的内部作业名、标签等信息有助于绘制内部系统图谱进行更有针对性的下一步攻击。注意很多人会混淆“未授权访问”和“远程代码执行RCE”。Pushgateway 这个漏洞本身不直接导致 RCE其危害主要体现在数据层面的机密性破坏和完整性破坏属于《网络安全法》和等保2.0中明确要求防护的风险。2.3 与相关热词漏洞的横向对比在排查时我习惯性地做了一次横向对比这有助于理解漏洞的共性和个性Nacos Namespaces 未授权访问类似也是管理接口默认缺乏认证可导致配置信息泄露甚至篡改。Swagger API 未授权访问开发接口暴露导致 API 结构、参数等内部信息泄露。Kibana 未授权访问可直连 ES 集群读取甚至修改索引数据。Redis 未授权访问可直接操作数据库get/set 数据甚至写入 SSH 公钥获取服务器权限。它们的共同点是服务本身功能强大但默认安装配置下安全边界是缺失的。Pushgateway 的特殊性在于它处理的是时序监控数据其安全风险更偏向于“数据欺诈”和“监控干扰”这种影响有时比直接的数据泄露更隐蔽、更棘手。3. 修复方案设计与选型考量确认漏洞后接下来就是制定修复方案。目标很明确为 Pushgateway 的访问加上一道可靠的身份验证和授权关卡。社区和实践中常见的方案主要有以下几种我逐一分析并说明我们的选型理由。3.1 常见修复方案对比方案实现方式优点缺点适用场景网络层隔离通过防火墙、安全组、K8s NetworkPolicy 限制源 IP。实现简单零性能开销。规则维护复杂在动态 IP 或微服务众多时易出错无法防止内网横向移动。推送端 IP 固定且少量的简单环境。反向代理 基础认证在 Pushgateway 前部署 Nginx/HAProxy配置 HTTP Basic Auth。部署简单通用性强可利用现有 LB 设施。密码明文传输需配合 HTTPS密码管理麻烦认证粒度粗全有或全无。快速临时加固或对安全要求不高的内部环境。反向代理 高级认证使用 Nginx 集成 LDAP、OAuth2 Proxy、或 mTLS 客户端证书认证。安全性高可与企业身份系统集成mTLS 安全性最强。配置复杂度高引入额外组件维护成本。中大型企业已有统一身份认证体系。Pushgateway 原生特性使用--web.config.file支持 TLS 和 Basic Auth需自行管理用户文件。原生支持无需额外组件。功能相对基础用户文件管理不便社区案例少。小规模部署希望保持组件简洁。服务网格如 Istio利用 Istio 的 AuthorizationPolicy 进行服务间认证授权。云原生原生方案与 K8s 集成度深策略声明式。架构复杂学习成本高适用于全网格化环境。已全面部署服务网格的云原生环境。3.2 我们的选型Nginx HTTPS Basic Auth 组合拳经过评估我们选择了“反向代理Nginx HTTPS HTTP Basic Authentication”的方案。理由如下技术栈匹配我们已有统一的 Nginx Ingress Controller 作为集群入口在此基础上扩展配置无需引入全新组件运维成本最低。快速生效该方案配置明确能够快速上线阻断风险符合安全应急响应的要求。够用的安全性虽然 Basic Auth 密码是 Base64 编码非加密但强制搭配 HTTPS后传输过程是加密的可防止密码嗅探。对于内部监控系统此安全级别已足够。清晰的权责由接入层负责认证Pushgateway 本身只负责业务处理指标符合关注点分离的原则。实操心得选型时一定要考虑团队的技术储备和现有基础设施。最“高级”的方案不一定是最合适的。例如如果团队没有 Istio 经验强行上马会带来巨大的运维复杂性和故障风险。我们的核心目标是“可靠地修复漏洞”而不是“炫技”。4. 实战修复步骤详解下面进入实操环节。假设我们原有的 Pushgateway 是通过 Kubernetes Deployment 部署在monitoring命名空间下服务名为prometheus-pushgateway。4.1 步骤一为 Pushgateway 服务创建认证密码首先我们需要生成一个用于 Basic Auth 的用户名和密码文件。这里使用htpasswd工具可通过apache2-utils或httpd-tools包安装。# 创建密码文件用户名为 pushuser htpasswd -c ./auth pushuser # 执行后会提示输入并确认密码重要安全操作生成的auth文件包含了用户名和加密后的密码。务必将其存入 Kubernetes Secret而非 ConfigMap因为 Secret 会进行 Base64 编码并提供一定的静默保护尽管在 K8s 中仍非完全加密。设置合理的文件权限如 600。# 在K8s中创建Secret kubectl create secret generic pushgateway-basic-auth --from-fileauth -n monitoring4.2 步骤二配置 Nginx Ingress 进行认证和 TLS 终结我们通过创建或修改一个 Kubernetes Ingress 资源来实现。这里假设你的集群使用nginx-ingress-controller。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: pushgateway-ingress namespace: monitoring annotations: # 关键注解启用Basic Auth并指定Secret nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: pushgateway-basic-auth nginx.ingress.kubernetes.io/auth-realm: Authentication Required - Pushgateway # 建议启用HTTPS重定向 nginx.ingress.kubernetes.io/ssl-redirect: true # 根据你的证书管理器配置可能还需要以下注解 # cert-manager.io/cluster-issuer: letsencrypt-prod spec: tls: # 配置TLS启用HTTPS - hosts: - pushgateway.your-domain.com # 你的域名 secretName: pushgateway-tls-secret # 指向存放证书的Secret rules: - host: pushgateway.your-domain.com http: paths: - path: / pathType: Prefix backend: service: name: prometheus-pushgateway port: number: 9091关键点解析auth-secret指向我们上一步创建的pushgateway-basic-authSecret。Ingress Controller 会自动读取其中的auth文件并加载到 Nginx 配置中。ssl-redirect强制将 HTTP 请求重定向到 HTTPS确保密码传输安全。tls配置域名和证书 Secret。证书可以通过 Cert-Manager 自动申请或手动创建 Secret 导入。路径问题这里配置的是/前缀意味着所有到达该域名的请求包括/metrics和/metrics/job/...都需要认证。这正是我们需要的。4.3 步骤三调整 Prometheus 拉取配置现在 Pushgateway 前端有了认证那么原本从 Prometheus 发起的拉取scrape请求也会被拦截。我们需要在 Prometheus 的配置中为这个 job 添加 Basic Auth 凭证。修改你的prometheus.yml或对应的 ConfigMapscrape_configs: - job_name: pushgateway honor_labels: true # 重要保留Pushgateway推送过来的job和instance标签 static_configs: - targets: [pushgateway.your-domain.com] # 使用Ingress域名 basic_auth: username: pushuser # 与之前创建的用户名一致 password: your-raw-password # 这里是明文密码注意安全踩坑预警这里有个大坑Prometheus 配置中的basic_auth.password需要的是明文密码而不是htpasswd生成的加密字符串。你需要将创建用户时输入的原始密码填在这里。这意味着密码以明文形式存在于 Prometheus 的配置存储中。为了缓解这个问题你可以使用单独的、强度较高的密码。确保 Prometheus 配置存储如 ConfigMap的访问权限严格控制。考虑使用 Prometheus Operator 的Secret字段来引用存储在 K8s Secret 中的密码部分版本支持。4.4 步骤四调整所有指标推送客户端的代码这是修复过程中最繁琐但至关重要的一步。所有向 Pushgateway 发送 POST 请求的客户端脚本、Java/Go/Python 应用等都需要在请求头中添加 Authorization 字段。以最常用的curl和 Pythonrequests库为例Bash/Shell脚本curl# 之前 curl -X POST http://pushgateway-ip:9091/metrics/job/my_job -d my_metric 1 # 之后 PUSHGW_PASSyour-raw-password # 建议从环境变量读取 curl -u pushuser:${PUSHGW_PASS} -X POST https://pushgateway.your-domain.com/metrics/job/my_job -d my_metric 1 # 注意协议变成了 HTTPS主机名变成了域名。Pythonrequestsimport requests from requests.auth import HTTPBasicAuth url https://pushgateway.your-domain.com/metrics/job/my_job data my_metric 1 auth HTTPBasicAuth(pushuser, your-raw-password) # 密码建议从配置或环境变量获取 response requests.post(url, datadata, authauth)重要提醒协议变更务必从http改为https。地址变更从直接的IP:Port改为通过 Ingress 暴露的域名。密码安全绝对不要将密码硬编码在代码或脚本中必须使用环境变量、配置中心或密钥管理服务来传递。批量更新需要梳理所有使用 Pushgateway 的业务方和团队推动他们进行改造。这是一个沟通和协作的过程。5. 修复效果验证与深度测试配置完成后绝不能仅仅检查服务是否正常就宣告成功。必须进行穿透性验证确保漏洞已真正修复且不影响正常功能。5.1 验证一未授权访问被阻断这是最直接的测试。尝试在不提供凭证的情况下访问 Pushgateway。# 测试1: 直接访问HTTPS端点应返回401 Unauthorized curl -I https://pushgateway.your-domain.com/metrics # 预期输出应包含: HTTP/2 401 或 HTTP/1.1 401 Unauthorized # 并且响应头应包含: Www-Authenticate: Basic realmAuthentication Required - Pushgateway # 测试2: 尝试使用错误的凭证 curl -u wronguser:wrongpass -I https://pushgateway.your-domain.com/metrics # 同样应返回 4015.2 验证二授权访问正常使用正确的凭证验证 Prometheus 拉取和客户端推送是否恢复正常。# 测试3: 使用正确凭证访问应返回200 OK及指标数据 curl -u pushuser:your-password https://pushgateway.your-domain.com/metrics | head -20 # 测试4: 模拟客户端推送一个测试指标 curl -u pushuser:your-password -X POST https://pushgateway.your-domain.com/metrics/job/test_verify/instance/hostname -d verify_metric 1 # 再次查询确认指标已存在 curl -u pushuser:your-password https://pushgateway.your-domain.com/metrics | grep verify_metric5.3 验证三Prometheus 拉取链路完整登录到 Prometheus UI或通过 Grafana检查Targets页面找到名为pushgateway的 job。其状态State应为UP。等待几分钟后在 Graph 页面查询up{jobpushgateway}其值应为1。这证明 Prometheus 能够成功通过认证拉取到 Pushgateway 的指标。5.4 验证四业务客户端推送无异常选择几个有代表性的业务推送客户端如关键的定时批处理任务观察其日志。修复后首次运行可能会因为认证失败而报错在更新代码和配置后应能恢复正常运行且无相关错误日志。同时在 Pushgateway 和 Prometheus 中应能查到该业务推送的最新指标。5.5 高级验证安全扫描复测将修复后的服务域名和端口重新加入到内部的安全漏洞扫描平台如 Nessus, OpenVAS, AWVS 或商业SAST/DAST工具的扫描任务中。运行一次专项扫描确认之前报告的“Prometheus Pushgateway 未授权访问”高危漏洞已消失或风险等级降为低/信息级。这是最权威的闭环验证。6. 生产环境部署的注意事项与避坑指南在实际生产环境中落地这套方案我遇到了不少细节问题这里总结出来希望能帮你绕过这些坑。6.1 密码管理的一致性问题我们选择了 Nginx Basic Auth那么密码会存在于至少三个地方初始生成的htpasswd文件存于 K8s Secret。Prometheus 配置中明文。所有推送客户端的配置中。如何管理统一密码源可以考虑使用 Vault 等密钥管理工具客户端和 Prometheus 都从 Vault 动态获取密码。但这涉及较大改造。折中实践我们采用了一个专用密码并利用 K8s Secret 的机制。将密码存入一个通用 Secret然后Ingress Annotation 通过auth-secret引用它。Prometheus 配置通过环境变量或 Volume 挂载方式从该 Secret 读取密码需确认你的部署方式是否支持。业务应用通过 K8s Downward API 或 Init Container 将密码注入到环境变量中。这样只需更新这个 Secret然后滚动更新相关 Pod就能完成密码轮换虽然过程依然繁琐。6.2 高可用与性能考量Pushgateway 前面加了 Nginx Ingress这实际上引入了一个新的单点吗并不会因为你的 Nginx Ingress Controller 本身应该是多副本部署的。但需要考虑Ingress Controller 性能额外的 Basic Auth 验证会带来少量的 CPU 开销每个请求都需要计算和验证哈希。对于监控系统这种高并发、小请求的场景需要关注 Ingress Controller 的 CPU 使用率和请求延迟。在我们的实践中开销可以忽略不计。Pushgateway 自身配置确保 Pushgateway 的 Deployment 配置了合理的资源请求和限制以及就绪和存活探针保证其自身稳定。6.3 客户端改造的兼容性与回滚推动所有客户端改造是最大的挑战。务必做好兼容性方案和回滚准备。灰度发布可以先修改 Ingress 配置同时支持带认证和不带认证的访问这仅是过渡方案。例如先配置认证但不强制让旧客户端继续用 IP:Port 访问保持网络隔离新客户端用域名认证访问。待所有客户端升级完毕后再关闭旧入口并强制认证。清晰的文档和通知编写详细的改造指南包含各语言客户端的代码示例并明确告知切换截止日期。监控告警在切换期间密切监控 Pushgateway 的请求错误率如 401 状态码数量和 Prometheus 对 Pushgateway 的拉取成功率。设置告警以便在出现大面积失败时能快速响应。6.4 长期安全增强建议Basic Auth HTTPS 是有效的但并非终极方案。从长远看可以考虑mTLS 双向认证为 Prometheus 和所有推送客户端颁发独立的客户端证书。Nginx 可以配置验证客户端证书从而实现更强大的身份认证且无需管理密码。这更适合机器对机器的通信场景。细粒度授权目前的方案是“全有或全无”。理想情况下可以区分“拉取者”如 Prometheus和“推送者”不同业务的客户端甚至对不同业务的推送路径进行授权。这可以通过更复杂的反向代理规则如根据路径进行不同的认证或使用 API 网关来实现但复杂度激增。定期安全审计将此类组件的安全配置如是否开启认证、TLS 版本、密码强度纳入常规安全审计和扫描范围防止配置漂移。7. 总结与核心体会回顾整个修复过程从触发告警到最终验证闭环花费了将近一周的时间其中大部分精力都耗在了“客户端改造”的沟通和协调上。技术方案本身并不复杂但如何在不影响业务监控的前提下平稳、彻底地落地才是真正的挑战。这次经历让我深刻体会到云原生环境下的安全绝不仅仅是安装一个安全软件那么简单。它要求运维人员必须具备“全链路”视角理解组件的真实行为不能只看文档的“如何使用”更要思考“在默认情况下它有多开放”。设计可落地的方案平衡安全性、复杂性和运维成本。最简单的方案往往是最可持续的。重视变更管理尤其是涉及多团队、多客户端的变更沟通、文档、灰度、监控、回滚预案一个都不能少。验证必须穿透不能只验证“功能正常”必须从攻击者视角验证“漏洞是否真的被堵上了”。最后给所有使用 Prometheus 生态的同学提个醒除了 Pushgateway也请检查一下你的Alertmanager、Grafana如果直接暴露、各种 Exporter如 node_exporter的访问控制配置。默认安装往往意味着默认不安全主动加固是云原生运维的必修课。安全就像氧气平时感觉不到但一旦缺失后果往往是致命的。