ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Elasticsearch安全加固:四大通信通道TLS/SSL配置实战

Elasticsearch安全加固:四大通信通道TLS/SSL配置实战 身边不少同事第一次给 Elasticsearch 做安全加固时都有过一种错觉把xpack.security.enabled开成true再建立一个超级用户就以为“安全通信”完事了。结果一到生产环境各种SSLHandshakeException、health check failed、节点之间互相握不上手才意识到 Elasticsearch 的安全通信通道配置根本不是一两个开关的事而是一整套需要按链路逐一落地的体系。这篇文章我想把 Elasticsearch 里最常见的“四大安全通信通道”拆开讲清楚。所谓四大通道我按工作中的实际数据流向划分HTTP/REST 通道、Transport 节点间通道、跨集群通信通道以及身份认证与授权通道。前三个是传输链路第四个是接入控制。每条链路都有各自的证书、各自的配置参数、各自最容易踩的坑。适合正在接手 ES 集群运维、打算从明文环境迁移到加密环境、或者被各种证书错误折磨到想砸电脑的同学参考。我会把原理、配置和实操经验一起给到尽量让你少走弯路。1. 先画地图Elasticsearch 集群里到底有几条“数据链路”需要保护1.1 三条数据流和一个身份入口要理解四个通道先看数据在 Elasticsearch 环境里是怎么流转的。ES 不是单机软件它默认就以分布式集群的形态在跑数据会在节点之间搬来搬去客户端也随时在往里写、往外查。如果把这些数据流画一张草图大概是这样的客户端到集群的 REST 流量应用、Kibana、Logstash、Beats以及你手动敲的curl全部走 HTTP 协议打到 ES 的 9200 端口。这条链路是所有人接触 ES 最多的入口也是安全配置上最容易被“裸奔”的地方。集群内部节点之间的流量ES 节点之间用 Transport 协议通信默认走 9300 端口。分片复制、副本分配、心跳检测、集群状态同步全都在这条链路上跑。如果这里不加密意味着你明文写入的数据会在内网里以明文方式复制到其他节点上。跨集群流量当你配置了跨集群搜索CCS或跨集群复制CCR时当前集群会作为 client 去连接另一个集群的 transport 端口。这条链路是“集群对集群”如果不单独做安全配置两个集群之间的数据交换就是裸奔状态。身份与权限校验上述所有流量进来之后ES 的安全模块都要做一次“你是谁、你能干什么”的核验。用户名密码、API Key、证书认证以及角色权限 RBAC都在这个环节生效。我见过太多只配了前三条链路、却在认证授权上偷懒的集群。比如用elastic超级用户跑所有业务或者应用侧账号给了superuser权限。这种“大门修得再牢固、屋里所有房间共用一把万能钥匙”的做法等于把前三条链路的保护全部打了折扣。所以我把认证授权也算作第四条通道它跟前面三条传输通道是并列关系缺一不可。1.2 版本差异决定配置方式7.x 手动开、8.x 默认开开工前必须先确认你手里的 ES 是哪个大版本的因为配置差别非常大。如果你用的是7.x安全功能默认是关闭的。你需要自己动手打开xpack.security.enabled然后逐个开启 HTTP、Transport 的 SSL再生成证书。这个版本里踩坑最多的是开了安全忘记给 Kibana 配置账号导致 Kibana 无法访问 ES或者只开了 HTTP 加密、Transport 还是明文集群内部数据裸奔。如果你用的是8.x情况完全不同。从 8.0 开始ES 默认开启安全功能首次启动会自动生成一套证书、自动创建elastic超级用户密码。表面看省事了实际麻烦在于默认证书的主机名可能跟你自己的域名/IP 不匹配首次启动时生成的密码如果你没保存好后面恢复超级用户又是一通折腾。所以 8.x 用户要做的事通常是用自动生成的配置跑通之后尽快替换成符合自己内网域名体系的正式证书。下面四章我就按四类通道逐一说明。配置示例以 elasticsearch.yml 为主版本差异我会专门标注大家按自己的版本对照使用。2. HTTP 通道给 9200 端口的明文 HTTP 穿上 HTTPS 这件防弹衣2.1 为什么它是第一个要处理的通道HTTP 通道是所有通道里最“显眼”的。你的业务方、监控系统、数据可视化工具几乎全都会连接 9200 端口。ES 原生 HTTP 接口返回的数据里经常直接带着业务内容如果这条链路是明文 HTTP等于有人在内网里交换机上做流量镜像就能直接看到你写的文档内容。给 HTTP 通道启用 TLS 之后连接串从http://变成https://客户端和服务端之间的数据就加密了。这一步做起来其实不难难点在于证书怎么来、客户端要不要校验服务端、Java 程序的信任库怎么设置。这三个点没想清楚后面全是幺蛾子。在动手之前先明确一个原则HTTP 通道的 TLS 主要保护“数据在传输过程中不被偷听”它不负责证明客户端是谁。客户端身份的证明要交给后面第五章的认证授权通道。2.2 证书生成与配置实操certutil 的正确用法官方提供的证书工具是elasticsearch-certutil。它的灵活性很高但也因为参数太多很多人第一次用就懵了。我给出一种最稳妥的生成方式# 生成 CA 证书只在第一次做时执行 bin/elasticsearch-certutil ca --p12 --out /etc/elasticsearch/certs/ca.p12 --pass ca_password # 生成 HTTP 通道证书 bin/elasticsearch-certutil cert \ --ca /etc/elasticsearch/certs/ca.p12 \ --ca-pass ca_password \ --p12 \ --name http-cert \ --dns es-node-1.example.local \ --dns es-node-2.example.local \ --ip 192.168.10.11 \ --ip 192.168.10.12 \ --out /etc/elasticsearch/certs/http.p12 --pass http_password这里有几个容易出错的地方我一条条说。--dns和--ip必须覆盖实际访问地址。如果你的集群前面有负载均衡负载均衡的虚拟 IP 和域名也必须加进去。否则客户端做主机名校验时会直接报No subject alternative names matching IP address之类的错误。ES 不推荐直接用同一个 p12 文件既当 keystore 又当 truststore但很多小团队图省事就这么干。实测可以跑通前提是文件里既包含自己的私钥证书、又包含 CA 公钥。我建议还是单独准备一个 truststore避免把私钥文件复制到所有客户端。--p12参数要保留这样生成的是 PKCS#12 格式Java 生态支持得最好。如果你生成的是 PEM 格式后面 Java 客户端配置时要写一长串ssl_certificate_authorities路径维护成本高得多。证书生成之后在elasticsearch.yml里这样配置 HTTP 通道的 SSLxpack.security.http.ssl: enabled: true keystore.path: /etc/elasticsearch/certs/http.p12 keystore.secure_password: http_password truststore.path: /etc/elasticsearch/certs/ca.p12 truststore.secure_password: ca_password注意keystore.secure_password里写的是 http.p12 的密码truststore.secure_password里写的是 ca.p12 的密码。这两个密码经常有人填反报错时提示keystore password was incorrect第一反应应该先去检查是不是把两处密码搞混了。2.3 应用侧连接从 Kibana 到 Spring Boot 的证书信任服务端配置完成只是第一步客户端也必须信任你的 CA 证书否则握手必失败。Kibana 侧需要把 CA 证书内容追加到 system truststore或者通过elasticsearch.ssl.certificateAuthorities指定。我更推荐后者不污染系统信任库elasticsearch.hosts: [https://es-node-1.example.local:9200] elasticsearch.ssl.certificateAuthorities: [/path/to/ca.crt] elasticsearch.ssl.verificationMode: certificateSpring Boot 项目里最常见的错误就是文章首段提到的那个e.elasticsearchrestclienthealthindicator : elasticsearch health check failed。这个报错几乎都出在同一个地方Spring Boot 的RestHighLevelClient或者ElasticsearchRestClient仍然在使用http://地址而服务端已经强制跳转 HTTPS或者地址写的是https://但是 Java 进程的 JVM 信任库并不认你的私有 CA。排查时先问自己三个问题地址协议对不对es.cluster-uri或elasticsearch.uris里写的是https://吗JVM 默认信任库认不认这个 CA如果用的是私有 CA 签发的证书必须把 CA 导入 JVM 的 cacerts或者在客户端代码里指定SSLContext.主机名对不对得上证书里的 SAN 有没有覆盖你配置里写的域名。还有一个非常坑的细节Spring Boot 的健康检查默认会请求/_cluster/health接口。如果你在 ES 侧开启了 HTTP 通道 TLS但健康检查间隔设置得太短ES 和客户端频繁做 TLS 握手会在高并发场景下造成 CPU 开销明显上升。我见过一个业务团队把健康检查间隔设成 1 秒十几个 pod 同时对 ES 打健康检查ES 节点的 CPU 直接被打到 60% 以上。后来把间隔放宽到 15 秒CPU 立刻降下来了。这个经验不值钱但真的很实用。3. Transport 通道节点与节点之间的私密对话3.1 transport 通信的安全风险比 HTTP 更隐蔽的泄露口很多团队配完 HTTP 通道就觉得万事大吉但我会提醒他们HTTP 通道只是客户端访问的窗口而集群内部节点之间的 Transport 通道才是数据量最大、最容易泄露的地方。ES 的每个分片都有副本。你写入 1 条数据至少在 primary 和 replica 上各存一份。这两份数据在节点之间的传输走的就是 Transport 通道。如果 Transport 通道是明文那么你在 HTTP 通道上的加密就只保护了“客户端写入 ES 的那一段”ES 内部把数据复制到其他节点时又是裸奔。这就像你在家门口装了监控但家里几个房间之间走动却不关门一样。Transport 通道的安全配置核心是让每个节点都能验证对端节点的身份并且让加密密钥只属于本集群。ES 通过节点证书的方式实现而不是简单的用户名密码。3.2 节点证书的生成与 verification_mode 选择Transport 证书的生成逻辑和 HTTP 证书类似但通常每个节点需要独立一张证书因为节点之间有双向证书校验。我习惯在管理机上统一签发bin/elasticsearch-certutil cert \ --ca /etc/elasticsearch/certs/ca.p12 \ --ca-pass ca_password \ --name node-1 \ --dns node-1.example.local \ --ip 192.168.10.11 \ --p12 \ --out /etc/elasticsearch/certs/transport-node-1.p12 --pass transport_password生成完以后把这张 p12 分发到对应节点。每个节点的 elasticsearch.yml 里这样写xpack.security.transport.ssl: enabled: true verification_mode: certificate keystore.path: /etc/elasticsearch/certs/transport-node.p12 truststore.path: /etc/elasticsearch/certs/ca.p12这里有一个关键参数xpack.security.transport.ssl.verification_mode。它有三个候选值full、certificate、none。none其实还验证了证书只是不做主机名校验基本不建议生产使用。certificate校验证书是否由受信 CA 签发但不管证书里的主机名和 IP 是否对得上。full是certificate加主机名校验要求证书里的 SAN 必须匹配节点的实际 hostname/IP。我个人建议如果节点 IP 和域名都稳定用full如果节点经常扩容、IP 变动频繁可以暂时用certificate但一定要明白你放弃了主机名验证这一层。很多团队第一次搭集群时用full后面扩容新加节点忘记在新节点证书里填正确 IP全部节点互相报unable to verify hostname就是这个原因。3.3 节点间证书不一致带来的“假集群”问题这里必须分享一个我踩过的坑某个集群一直运行在明文 Transport 模式下后来为了安全加固我准备逐节点开启 Transport TLS。一开始我天真地以为ES 支持热更新 SSL滚动重启节点就能平滑切换。结果我只改了其中一个节点的transport.ssl.enabled它的 Transport 端口立刻开始用 TLS 协议握手而其他节点还在用明文协议。结果这个节点跟其他节点彻底失联整个集群开始出现master not discovered的错误。后来我学乖了Transport 通道的安全配置切换必须所有节点保持一致不能在集群中混用明文和加密。要么全开要么全关。如果你必须在生产环境滚动切换建议搭一个临时的新集群做迁移用跨集群迁移的方式把数据搬过去比原地滚动改造要安全得多。此外ES 节点与节点之间的握手是有超时时间的。如果证书文件过大、密码错误导致握手迟迟不能完成会出现requested to send 100MB of data...之类的异常。排查时除了看证书还要检查节点之间的 MTU 问题。这个我后面在常见问题表格里一起整理。4. 跨集群通道CCS/CCR 场景下的独立安全域4.1 跨集群通信的安全模型每个集群都有自己的信任域当你需要做跨集群搜索CCS或跨集群复制CCR时集群 A 要去连接集群 B 的 Transport 端口。这时候如果每个集群都启用了节点间加密那么“集群 A 的某个节点”这个身份在集群 B 看来就是一个外部客户端。跨集群通信的安全模型并不复杂核心原则是用集群 B 的 CA 去签发集群 A 使用的客户端证书或者让集群 B 的 truststore 信任集群 A 的 CA。换句话说不同集群之间的证书信任关系是可以选择“单向信任”还是“双向信任”的而没有必要让所有集群共用同一个 CA——那样反而扩大了一个 CA 泄露的爆炸半径。我见过比较规范的实践是每个集群使用独立的 CA然后在需要互访的集群之间做 CA 互换。比如集群 A 持有自己的 CAa集群 B 持有自己的 CAb要让 A 访问 B就把 CAb 导入 A 对 B 的 truststore把 A 的证书信息在 B 侧登记为受信客户端。4.2 remote_cluster SSL 配置实操新版 ES8.11 之后把跨集群 SSL 单独拆出了一个配置域xpack.security.remote_cluster.ssl。它和 Transport 的 SSL 是独立控制的。举例如果集群 B 的节点证书是用 CA_b 签发的那么集群 A 想要连 BA 的配置大致如下xpack.security.remote_cluster.ssl: enabled: true verification_mode: certificate truststore.path: /etc/elasticsearch/certs/ca_b.p12 truststore.secure_password: ca_b_password而集群 B 如果想要允许集群 A 的节点连进来B 侧的 truststore 需要信任 A 的 CAxpack.security.remote_cluster.ssl: enabled: true verification_mode: certificate truststore.path: /etc/elasticsearch/certs/ca_a.p12 truststore.secure_password: ca_a_password配置完 SSL 后再用 API 注册远程集群curl -X PUT https://cluster-a:9200/_cluster/settings \ -u elastic:password \ -H Content-Type: application/json \ -d { persistent: { cluster.remote.cluster_b: { seeds: [cluster-b-node1:9300, cluster-b-node2:9300], mode: sniff } } }这里有个细节seeds的端口是 transport 的 9300不是 HTTP 的 9200很多人配置跨集群时会把 HTTP 端口当成传输端口填进去结果总是连接超时。4.3 跨集群调试实录证书信任报错的典型特征跨集群调试时最容易出现的报错是Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target这个报错说明发起方的 truststore 里不信任对端证书链。排查步骤我固定按三走确认集群 B 的节点证书是由哪个 CA 签发的用 openssl 或 keytool 查看。把那个 CA 导入集群 A 的 remote_cluster truststore。确认 verification_mode 是否符合你的预期。如果 mode 是full还要确认 seeds 里的主机名或者 IP 和集群 B 节点证书的 SAN 匹配。跨集群还有一个容易忽略的问题如果集群 A 和集群 B 的节点名重合比如都是“node-1”那么在集群 B 侧看集群 A 的连接日志时很容易误判为本地节点连接异常。建议在不同的集群里设置不同的节点名前缀比如cluster-a-node-1、cluster-b-node-1排查问题时日志会清晰很多。5. 认证授权通道入口处的身份核验与门禁5.1 认证方式怎么选file/native/LDAP/API key传输通道都加密之后剩下的问题是谁能进这道门。ES 的认证体系支持多种 realm域可以简单理解为“多套用户名单和验证规则”。file realm基于本地 user 文件适合初始化集群、搭建测试环境。native realm用户信息存在 ES 内部索引中可以通过 API 动态管理用户和角色是最常用的内置认证方式。LDAP / Active Directory企业内通常用这种方式对接统一身份源所有账号密码都走公司已有的目录服务。SAML / OIDC主要给 Kibana 做单点登录使用对普通应用访问不太适用。API Key应用连 ES 时更推荐用 API Key而不是直接把用户名密码放在代码或配置项里。我遇到过几个团队直接把所有业务系统账号都建成 native 用户一个业务系统一个密码密钥管理混乱。后来我改成了 API Key 为主每个业务系统创建独立角色然后通过 API 签发 API Key把 Key 分发给对应应用。这样即使某个应用的 Key 泄露只需要删除这个 Key不会影响其他系统。创建 API Key 的请求非常简单curl -X POST https://es:9200/_security/api_key \ -u elastic:password \ -H Content-Type: application/json \ -d { name: order-service-key, role_descriptors: { order_service_role: { cluster: [monitor], indices: [ { names: [orders-*], privileges: [read, write] } ] } }, expiration: 30d }这里可以设置过期时间同时能把权限范围收窄到指定索引比全局用户名密码安全很多。5.2 RBAC 权限模型落地不要把所有鸡蛋放在 superuser 一个篮子里权限模型我坚持一个原则宁可一开始多花半小时拆角色也不要在五个系统里共用同一个超级用户。ES 的角色模型是典型的 RBAC按 index、cluster、application 三个维度划分权限。最基础的划分思路监控角色只有monitor集群权限和read索引权限给监控系统用。数据写入角色只能write到指定索引不给delete。数据读取角色只能read指定索引不给写入权限。管理角色manage_index_templates、manage_ilm等给运维同学用。在 Kibana 管理界面可以创建角色也可以通过角色 API 批量创建。我更推荐用 API 脚本统一管理因为角色变更记录可以走 Git审计时能看得到每次改了哪些权限。这里提醒一句superuser角色虽然方便但它同时拥有 ES 集群最高管理权限。如果某个应用服务被攻破攻击者拿到这个角色就能删索引。我经手的项目里凡是能用自定义角色解决的需求一律不给superuser。5.3 审计与合规连接日志是最后一道防线认证授权通道的最后一环是审计。ES 可以开启 audit log记录哪些用户、在什么时候、通过哪个 IP、访问了哪些索引、执行了什么操作。xpack.security.audit.enabled: true开启之后会在日志目录下生成 audit 日志。实际运维中它最大的用处不是“人工去看”而是对接告警比如一分钟内某个 IP 连续出现多次认证失败大概率是有人在做口令爆破比如某个普通业务账号突然去访问了security系统索引大概率是权限配置出了问题。顺带一提审计日志本身也是敏感数据需要严格的权限控制。生产环境建议直接把 audit 日志输出到独立日志系统或 SIEM不要跟业务日志混在同一个文件里否则排查问题时日志量太大会让人头大。6. 完整配置模板与排查实录6.1 一份可以直接抄作业的配置模板把四个通道合在一起一份完整的最小安全配置模板如下假设已生成 ca.p12、http.p12、transport-node.p12cluster.name: production-cluster node.name: node-1 network.host: 0.0.0.0 discovery.seed_hosts: [node-1, node-2, node-3] xpack.security.enabled: true xpack.security.http.ssl: enabled: true keystore.path: /etc/elasticsearch/certs/http.p12 keystore.secure_password: http_password truststore.path: /etc/elasticsearch/certs/ca.p12 truststore.secure_password: ca_password xpack.security.transport.ssl: enabled: true verification_mode: certificate keystore.path: /etc/elasticsearch/certs/transport-node.p12 keystore.secure_password: transport_password truststore.path: /etc/elasticsearch/certs/ca.p12 truststore.secure_password: ca_password xpack.security.remote_cluster.ssl: enabled: true verification_mode: certificate truststore.path: /etc/elasticsearch/certs/ca_remote.p12 truststore.secure_password: ca_remote_password如果你用了 Docker Compose 或 Kubernetes 部署需要把证书目录通过 volume 挂载进容器。不同的部署方式证书路径不一样但核心配置参数完全相同。我之前在 Docker 部署里踩过的坑是容器内路径没写对ES 启动时直接file does not exist。这个错误不算难但会让人误以为是权限问题排查半天才发现是挂载路径给错了。6.2 四通道自检清单配置完之后我一般会按照下面这个清单逐条验证防止漏配检查项命令/方法预期结果HTTP 通道是否启用 HTTPScurl -k https://localhost:9200返回 ES 版本信息且连接是 httpsHTTP 通道证书 SAN 是否覆盖访问域名openssl x509 -in http.pem -text -noout或 keytool 查看域名/IP 与访问地址匹配Transport 节点间加密是否生效查看启动日志中transport.ssl相关日志出现SSL ... enabled类似信息节点证书互信是否正常查看是否没有握手失败日志集群状态变绿跨集群 SSL 证书信任执行GET _remote/info显示远程集群连接成功认证账号可用curl -u user:pass https://es:9200返回正常结果业务账号权限是否符合预期用业务账号尝试越权操作应返回 403 权限不足不需要的角色/Key 是否已清理GET _security/role、GET _security/api_key无冗余高风险账号6.3 高频问题速查表最后把实际工作中遇到的高频问题整理成一个速查表方便遇到问题时直接对号入座现象可能原因排查方向health check failedSpring Boot 连接地址是 http或 JVM 不信任私有 CA检查https://、导入 CA 到 JVM cacertsSSLHandshakeException: PKIX path building failed客户端 truststore 没有对端 CA确认对端证书链导入对应 CANo subject alternative names matching IP证书 SAN 未包含该 IP/域名重新生成证书补全 SAN节点之间互相发现不了master not discoveredTransport SSL 配置不一致全部节点统一 SSL 配置滚动重启启用安全后写入变慢、bulk 大量 rejectTLS 握手开销、线程池队列打满、segment merge 频繁先看 bulk queue/reject、CPU/iowait、segment merging 指标把 bulk 批次和 refresh interval 调优跨集群连接超时seeds用了 9200 端口改为 9300 transport 端口API Key 泄露未设置过期时间或权限过大立即删除旧 Key重新签发并收窄 role打开避坑那里我觉得还得再强调一次所有 SSL 密码、keystore 密码尽量放在 ES 的 secure settings 里通过bin/elasticsearch-keystore add添加不要直接明文写在elasticsearch.yml。否则你一份配置仓库泄露出去等于连私钥密码一起送给了别人。这个习惯我从第一次被提醒之后就一直坚持哪怕是测试环境也走这个流程避免坏习惯被带到生产。我在实际配置中还有一个体会四大通道的安全配置前三条传输通道一定要在集群“还是空的时候”做好。等到业务数据都进去了、在线业务天天读写的时候再去补 SSL你会面临滚动升级、节点不兼容、客户端批量改配置等一堆问题。集群初建时多花半小时后面能省下几个通宵。最后分享一个小技巧如果你不确定自己的证书链路是否完整用keytool -printcert -file或openssl verify把证书链从叶子证书到根 CA 逐级校验一遍如果校验报错大部分情况下都是 CA 证书顺序放反了。把 CA 放在 truststore、把节点私钥放在 keystore、把客户端地址写进 SAN这三件事做到位四大通道的安全配置基本就不会翻车了。
返回列表