早几年部署Elasticsearch很多团队都是“裸奔状态”只要知道9200端口谁都能随意查询、修改甚至删除集群里的数据。数据不出事还好一出事就是重大事故。后来Elasticsearch把X-Pack里的安全认证功能免费开放之后给ES加认证这件事基本成了部署标配。这篇博文不聊概念只讲从零给ES添加认证的完整过程包括用户、角色、Kibana接入、Java客户端接入以及我实际踩过的几个坑。如果你准备在生产环境启用ES认证或者刚学ES但需要马上落地这篇内容可以直接照着操作。1. 认证添加前后的差别与整体设计思路1.1 不加认证时到底有多危险默认安装的Elasticsearch没有任何访问控制它监听在9200端口上任何能够触达这个端口的请求都能直接执行操作直接GET /_all/_search读取所有索引里的业务数据直接DELETE /index-*删除整个索引直接PUT /_cluster/settings修改集群配置甚至关掉分片分配这些操作不需要账号、不需要密码、不记录是谁做的。如果集群IP暴露到了公网那就是给攻击者送数据还会被人拿去挖矿、勒索。就算只在内网内部人员误操作删除索引的案例也不少。所以从安全角度讲认证不是“要不要开”的问题而是“什么时候开”的问题。开启认证之后ES会要求每一个请求都携带合法凭证没有凭证一律返回401。同时开启安全功能后还支持基于角色的访问控制可以做到“这个用户只能读、那个用户只能写、这个服务只能访问某个前缀索引”比裸奔时的一刀切强太多了。1.2 认证与授权的三层模型ES的权限体系可以类比成“门禁卡 房间权限”的关系认证Authentication确认你是谁通常通过用户名密码或证书完成授权Authorization确认你能干什么由角色和权限比特位控制底层模型分三层用户User唯一身份标识绑定一个或多个角色角色Role定义一组“集群权限”和“索引权限”的集合权限Privilege具体操作级别比如read、write、manage、all等在Elasticsearch 6.8和7.x中安全模块的核心是X-Pack需要在elasticsearch.yml里手动开启。8.x则默认开启安全。无论是哪个版本配置的思路都一样先建角色再建用户最后把角色挂到用户身上。千万不要在业务代码里用超级管理员elastic直连集群那是运维专用账号权限太大一旦密钥泄露整个集群都完了。2. 认证方式选型与核心原理2.1 Native原生认证最常用也最推荐原生认证是ES内置的用户存储方案用户名、密码、角色关系都保存在ES内部分配的索引里。7.x中对应的索引名是.security-78.x是.security-8。它的特点是不需要额外搭建外部系统可以通过REST API动态创建和管理用户改完立即生效密码以哈希形式存储ES自身负责校验在单机、中小规模集群或者没有统一账号体系的场景下直接用原生认证就够了。它唯一的弱点是“账号信息存在ES里”如果攻击者能拿到ES文件目录权限安全也就无从谈起但这已经是物理防护范畴了。2.2 File文件认证离线环境的好选择文件认证方式把用户和角色写进users、users_roles两个文件里存在各节点的配置目录下。它不依赖ES索引因此ES还没起来时也能校验、能启动。它的缺点也很突出每次增删用户都要编辑文件、同步到所有节点而且修改后需要重启或者执行elasticsearch-users命令刷新不适合频繁变动的业务环境。一般只在测试、离线环境或者ES本身还没正常启动时应急用。2.3 LDAP/AD、证书和JWT等高级方式如果公司已经有统一的LDAP或Active Directory账号体系可以配置LDAP Realm让ES对接外部目录服务。密码校验发生在LDAP端ES这边只做用户搜索和角色映射账号的增删改都发生在LDAP里ES不用管。PKI证书认证则是基于双向TLS客户端必须持有CA签发的证书才能访问安全性最高但证书的下发、轮转、吊销都是额外工作量适合金融或高安全场景。JWT方式适合已经接入SSO单点登录系统的团队。做一个简单的选型对比认证方式优点缺点适用场景Native配置简单、API可控、动态生效账号信息存在ES索引中绝大多数中小集群File不依赖ES索引、启动前可用修改麻烦、同步麻烦离线、测试、应急LDAP/AD复用企业账号体系依赖外部服务可用性企业内部统一认证PKI安全等级高、双向校验证书管理复杂金融、高安全要求JWT支持SSO单点登录需要额外权限映射已有统一认证平台实际工作中我用得最多的是Native对外暴露的集群一般还会在Native上面叠一层TLS证书保证密码不会明文穿越网络。3. 实操从零配置Elasticsearch认证3.1 环境准备与节点基础配置不同系统下ES的配置文件路径不同但配置项完全一致Linuxtar包部署$ES_HOME/config/elasticsearch.ymlLinuxAPT/RPM安装/etc/elasticsearch/elasticsearch.ymlWindows$ES_HOME\config\elasticsearch.ymlDocker容器通过挂载卷或者环境变量传入Windows环境有几个坑先说明。ES的bat脚本是bin\elasticsearch.bat启动前建议用管理员身份运行避免文件访问权限问题。另外目录路径不要出现中文或空格我之前在一台机器上把ES解压到了C:\Program Files\elasticsearch-7.17.0结果启动时bin目录的路径解析就出了问题后来换到D:\es\elasticsearch-7.17.0才正常。Windows下JVM堆内存默认可能是固定的1GB建议在jvm.options里按机器内存调一下启动更快也更稳。Linux下面要注意的是最大文件描述符数和虚拟内存区域数。启动前分别执行ulimit -n 65535 sysctl -w vm.max_map_count262144不然节点起来之后很快会在日志里看到max file descriptors的bootstrap check报错这跟认证配置无关但会挡在你动手之前。3.2 修改配置文件开启安全认证找到elasticsearch.yml加入以下内容xpack.security.enabled: true xpack.security.transport.ssl.enabled: true第一行开启安全功能第二行开启集群节点之间的TLS加密通信。千万别小看这第二行。在7.x和8.x里如果只开了认证不开启transport层TLS也不会报告任何显眼的错误但节点与节点之间的通信用的是普通端口密码信息在传输过程中有泄露风险。更麻烦的是8.x以后如果没配transport TLS集群多节点根本没法正常加入报错信息绕来绕去最后还是回到这个配置上来。如果你用的是8.0及以上版本安装包默认已经开启了安全认证不需要再手动改这两行。但要注意8.x在启动时会在控制台输出一个临时密码如果那个密码没记下来后续得用elasticsearch-reset-password重置。3.3 初始化内置用户密码改完配置文件后首次启动会报错提示security索引未初始化这是正常的。需要先停掉ES然后执行密码初始化命令bin/elasticsearch-setup-passwords interactive这个命令会列出所有内置用户比如elastic、kibana_system、logstash_system、beats_system、apm_system让你逐个输入新密码。要注意以下几点所有内置用户都必须设置密码不能只设置elastic密码不要用特殊字符太多比如、;这种在URL拼接和配置文件解析时容易出问题如果集群是多节点需要先在所有节点的配置里开启安全并分发好证书然后只在一个节点上执行这个命令即可命令执行完毕后再启动ES此时用curl -u elastic:密码就能访问了curl -u elastic:yourpassword http://localhost:9200/_cluster/health?pretty如果返回包含status : green或status : yellow的JSON说明认证已经生效了。3.4 创建业务角色和用户认证生效只是第一步接下来要按需创建合适的角色和用户。先创建一个只读角色允许访问logs-*前缀的索引curl -u elastic:yourpassword -X PUT http://localhost:9200/_security/role/blog_reader -H Content-Type: application/json -d { cluster: [], indices: [ { names: [logs-*], privileges: [read] } ] }再创建一个能写入但不能删除的角色curl -u elastic:yourpassword -X PUT http://localhost:9200/_security/role/blog_writer -H Content-Type: application/json -d { indices: [ { names: [logs-*], privileges: [create_index, write, create] } ] }然后创建用户把角色挂上去curl -u elastic:yourpassword -X PUT http://localhost:9200/_security/user/reader_user -H Content-Type: application/json -d { password: reader_pass_2024, roles: [blog_reader] }这样reader_user就只能读取logs-*索引删除和写入都会被拒绝。这种最小权限原则特别重要我在生产环境里都是这么设计的。业务服务只需要写入权就只给写入权需要读取的报表系统给只读权避免出现一个账号把所有索引都操作一遍的情况。3.5 Kibana接入认证配置Kibana本身没有自己的用户体系它只是通过服务账号去访问ES然后把登录页展示给用户。在kibana.yml里配置elasticsearch.hosts: [http://localhost:9200] elasticsearch.username: kibana_system elasticsearch.password: 你设置的kibana_system密码这里有个特别容易搞混的点kibana_system是Kibana用来连接ES的服务账号不能直接用它在浏览器里登录Kibana页面。页面登录要用elastic或者新建一个具备Kibana权限的业务用户比如新建一个角色curl -u elastic:yourpassword -X PUT http://localhost:9200/_security/role/kibana_admin_role -H Content-Type: application/json -d { cluster: [monitor], applications: [ { application: kibana-.kibana, privileges: [all], resources: [*] } ] }然后把该角色授予用户或者直接给用户挂内置角色kibana_admin。如果你用kibana_system去登录页面会一直提示认证失败这一点从参数名就能看出来不是一个用于人机交互的账号。3.6 Spring Boot / Java客户端接入认证Java生态里最常见的是Spring Boot连接ES。7.x场景用的是High Level REST Client需要在配置里带上认证凭证spring: elasticsearch: uris: http://localhost:9200 username: reader_user password: reader_pass_2024如果ES开启了TLS这里还有一个关键点证书校验。生产环境最好把ES的CA证书导入Java信任库或者给客户端提供一个信任证书。具体配置可以做成下面的样子Configuration public class EsConfig { Bean public RestHighLevelClient restHighLevelClient() { final CredentialsProvider credentialsProvider new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(reader_user, reader_pass_2024)); RestClientBuilder builder RestClient.builder( new HttpHost(localhost, 9200, http)) .setHttpClientConfigCallback(httpClientBuilder - { httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); return httpClientBuilder; }); return new RestHighLevelClient(builder); } }如果是ES 8.x官方推荐用新的Elasticsearch Java Client认证方式大同小异传入BasicAuthProvider即可。这里提醒一句客户端版本尽量和ES服务端版本保持一致。7.x客户端去连8.x服务端即使只是做查询也可能在响应的序列化字段上炸出奇奇怪怪的异常排查起来非常费劲。3.7 DBeaver等数据库工具连接DBeaver连接ES需要先安装Elasticsearch驱动连接URL填jdbc:elasticsearch://localhost:9200/用户名和密码填刚才创建的业务账号。DBeaver默认会走JDBC方式如果你给ES开启了HTTP层TLS也就是xpack.security.http.ssl.enabled: true那URL要改成https并且还得处理证书。DBeaver在SSL选项卡里可以选择“跳过主机名校验”但对于自签证书最省心的做法是把ES的CA证书导入DBeaver所在机器的JRE信任库然后重启DBeaver。否则即使你把用户密码填对了连接也会一直卡在“握手失败”。4. 常见问题与排查技巧实录4.1 用户名密码都对还是报401这个情况我见过不少排查的时候先区分问题出现在认证层还是网络层先确认ES日志里有没有明确的authentication failed字样再用curl -u手动验证一遍凭证是否真的正确如果是7.x之后默认开启了TLS检查客户端URL是不是用了https密码里如果有特殊字符比如在命令行里没加引号会被解析成主机名的一部分也会导致401还有一个隐蔽问题如果同时配置了file realm和native realm默认顺序是file在前。当你在users文件里也定义过同名用户ES会优先校验file里的密码native里改的密码就失效了。生产环境一般建议只保留一种realm或者调整顺序避免冲突。4.2 忘记elastic密码怎么办ES 8.x自带了重置命令bin/elasticsearch-reset-password -u elastic执行后会要求输入当前集群的相关信息如果是本地测试环境直接回车确认即可。8.x之前的版本比较麻烦需要临时关闭安全重启再用文件方式重置密码。步骤大概是停止ES在elasticsearch.yml里临时把xpack.security.enabled改成false启动ES等集群状态恢复执行bin/elasticsearch-users useradd elastic -p 新密码注意这个命令操作的是file realm跟原生认证的存储位置不同停ES把配置改回true再启动这样操作存在一个窗口期ES在安全关闭状态下相当于裸奔内网任何人都能访问只适合测试环境生产环境尽量通过快照或证书恢复来处理不要走这条路。4.3 恢复数据时认证失效了结合热词“elasticsearch 恢复数据”来说一个真实场景。通过快照恢复集群时如果快照里包含了.security-*索引ES会把当时的用户、角色、密码一并恢复回来。这会导致你恢复完发现原来改过的密码全被“时间回溯”了Kibana也连不上。更麻烦的是如果快照里的安全索引和当前版本不兼容ES启动时可能会反复报security索引异常。恢复数据的时候建议把安全索引排除在外{ indices: [*, -.security*], ignore_unavailable: true, include_global_state: false }如果已经恢复坏了最省事的办法是重新走一遍elasticsearch-setup-passwords interactive先把内置用户密码重置一遍再重新创建业务用户。4.4 开启认证后连接变慢了加认证确实会带来性能损耗尤其开启了TLS后每次建连都要做证书握手整体性能下降10%-20%是正常的。我自己在压测时观察到小请求场景下损耗更明显因为建连开销占比高。应对办法是让客户端尽量复用长连接不要每次请求都创建新的REST Client或连接池。Spring Boot里配置连接池的最大连接数和空闲时间基本能缓解大部分延迟问题。4.5 快速排查速查表症状可能原因排查动作401 Unauthorized账号密码错误、realm冲突、URL解析问题curl带-u加引号测试看ES日志403 Forbidden角色权限不足检查角色定义和索引名是否匹配Kibana白屏/不断重定向kibana_system密码错误或服务未启动看Kibana日志确认认证配置集群无法启动没有先执行setup-passwords启动前初始化内置用户密码恢复数据后密码全失效.security索引被快照恢复恢复时排除.security*索引实际踩过几次坑之后我总结了一条经验所有认证相关的配置都要做“变更前备份”也就是在改动elasticsearch.yml、执行密码重置操作之前先把原配置和密码记录复制到安全的地方。ES的数据丢失还可以从快照恢复但如果认证配置丢了、密码没了整个集群就真的进不去了。配置认证的过程并不复杂核心就是“用户-角色-权限”这一套逻辑只要理清账号归属和权限边界后面基本一劳永逸。