1. 默认裸奔的集群:为什么Elasticsearch上线第一天就被要求加认证
如果你刚接触Elasticsearch,大概率会经历这么一幕:照着官方文档装好ELK,启动瞬间看到那个经典的“You Know, for Search”,然后高高兴兴地打开http://localhost:9200,发现所有索引都能直接查、直接删,连账密都不用输。测试环境这么玩很爽,但一旦这个端口暴露到公网或局域网,基本等同于把你的数据文件放在马路上。我见过不止一个团队,因为嫌认证麻烦,把Elasticsearch裸奔了几个月,直到某天发现某个索引被清空,甚至被塞进了挖矿脚本,才想起来当初应该加认证。
所谓“认证添加”,本质上就是启用Elasticsearch的Security特性(以前叫X-Pack Security,现在已经免费开放),让每个请求都带上身份标识,只有通过验证的用户才能访问对应资源。认证解决的是“你是谁”的问题,配合后面的授权(RBAC),才能解决“你能干什么”的问题。这篇内容我按实际动手的顺序来写:先说清楚为什么要加、加之前要准备什么,再手把手走一遍内置用户和自定义用户的操作流程,最后是角色权限配置、常见的翻车现场以及接入方的适配。适合正在搭ES环境、刚被安全扫描报告逼着加认证的运维和开发同学,也适合需要给已有集群“补课”的团队参考。
先说一个容易让新手上头的点:ES的认证不是一个开关能一键搞定的。它牵扯到HTTP层和Transport层两层安全,生产环境还得处理证书、节点间加密,不是设置一个密码就算完。我接下来写的内容,是我在一台8G内存的CentOS测试机上加三节点集群时摸出来的完整流程,单节点和集群都适用,只是需要注意的地方不同。
2. 动手前的准备:版本差异、配置文件与证书方案
2.1 确认你的ES版本和许可证范围
开始配置之前,必须先确认你的Elasticsearch版本。为什么版本重要?因为不同大版本,安全特性的默认行为差别很大。
- 7.x系列:Security默认关闭,需要手动在
elasticsearch.yml里设置xpack.security.enabled: true。7.1之后基础安全功能免费。 - 8.x系列:默认开启Security,而且首次启动会自动生成一套TLS证书和内置用户密码,命令行输出里会打印。如果你是从8.x起步,反而少了“手动开启”这一步,但要处理一堆自动生成的凭据,很多人不习惯。
- 6.x及更早:需要安装单独的X-Pack插件,而且部分功能收费,我不建议新项目再用了。
如果你用的是8.x,第一次启动时终端会直接显示类似The generated password for the elastic user is XXXXX这样的信息,建议立即抄下来。如果不小心关了终端别慌,可以执行bin/elasticsearch-reset-password -u elastic重置。
我这次主要基于7.x来写,因为8.x的默认开启已经把流程简化了不少,7.x则需要你手动走完整个开启链,更能理解底层原理。许可证方面,从7.1开始,Security的认证和授权功能已经免费,不过一些高级特性(比如基于LDAP/AD的集成或SSO)可能需要白金版试用。我们自己搭环境用免费版足够了。
2.2 elasticsearch.yml里需要改哪些配置
在加认证前,先想清楚一个问题:你只是要HTTP层的用户密码认证,还是连节点间通信的Transport层也要加密?如果只是在单机上测试,只开HTTP认证也能跑,但一旦涉及两个以上节点,就必须处理Transport层的TLS,否则节点之间会因为安全配置不一致而互相拒绝。
我的建议是,哪怕只有一台机器,也把Transport层的TLS一起配了。因为你后面很可能要扩容,到时候补配置要重启集群,平白多一次半夜排队上线。何况自签证书的成本很低,用官方工具几分钟就能生成。
下面是一份最小化的elasticsearch.yml配置(以7.17为例):
cluster.name: es-test-cluster node.name: node-1 network.host: 0.0.0.0 http.port: 9200 xpack.security.enabled: true xpack.security.transport.ssl.enabled: true xpack.security.transport.ssl.verification_mode: certificate xpack.security.transport.ssl.keystore.path: elastic-certificates.p12 xpack.security.transport.ssl.truststore.path: elastic-certificates.p12这里有几个点要说明:
network.host不要随便暴露到公网。如果你只在服务器本机用,可以写成127.0.0.1,或者用防火墙限制来源IP。真正要暴露到内网访问,请搭配Kibana和反向代理一起用,ES这个端口越少人知道越好。xpack.security.transport.ssl.verification_mode: certificate表示节点间验证证书,但不验证域名。如果主机名容易变化,用这个模式能避免很多麻烦。如果必须严格校验hostname,可以改成full。- 证书文件路径是相对
ES_PATH_CONF(通常是$ES_HOME/config)的,所以路径不要写错。如果写在其他目录,最好用绝对路径。
2.3 用官方工具生成节点间通信证书
ES自带的elasticsearch-certutil就是干这个的。生成证书前,确保所有节点都能访问到同一个证书文件。我的做法是在第一台节点上生成,然后拷到其他节点。
cd $ES_HOME/bin ./elasticsearch-certutil cert -out /etc/elasticsearch/elastic-certificates.p12 -pass ""-pass ""表示证书密码为空。生产环境建议你设一个强密码,但那样每个节点的elasticsearch.yml里还要额外声明xpack.security.transport.ssl.keystore.password和xpack.security.transport.ssl.truststore.password。测试环境空密码最省事。
执行完之后,elastic-certificates.p12就是一个同时包含私钥和证书的PKCS12格式文件。把它复制到每个节点config目录下,并确保运行ES的用户有读权限。证书生成完毕,不要手贱去解压p12或者转成别的格式,ES直接能认。
如果你以后要加新节点,最好用同一个证书文件,不要每个节点单独生成。单独生成意味着各节点需要互相导入对方的证书,麻烦不说,还容易漏。
3. 实操:用内置用户体系给Elasticsearch加上认证
3.1 设置内置用户的密码
配置文件改好、证书就位、节点重启起来之后,ES的HTTP接口会立刻拒绝你。比如你curl localhost:9200,会收到一条security_exception错误,提示内容大致是“missing authentication credentials”。此时认证已经被激活,但内置用户还是默认的空白密码,所以下一步就是给内置用户设置密码。
官方提供了两条路:auto和interactive。
bin/elasticsearch-setup-passwords auto这条命令会为elastic、kibana_system、logstash_system、beats_system、apm_system、remote_monitoring_user等一组内置用户分别生成随机强密码,然后在终端一次性列出来。优点是快,缺点是我这种记性差的人很容易把终端关了才想起来没保存。所以我更推荐:
bin/elasticsearch-setup-passwords interactive交互模式会让你挨个输入每个用户的密码,适合需要对接多个组件、想把密码统一管理的场景。比如Kibana需要kibana_system的密码,Logstash需要logstash_system的密码,如果你用auto模式,必须把那串随机密码抄进Kibana的配置里。交互模式则可以统一设成带特定规律的密码,后续维护省心不少。
填密码的时候有两点注意:
- 每个用户的密码建议不一样,且不要用弱密码。虽然测试环境里1qaz2wsx这种能用,但集群一旦被扫到,几乎瞬间被爆破。
- 记好密码与用户的对应关系。后面Kibana、Beats、Logstash、任何排查环节都要用。
设置完以后,用curl验证一下:
curl -u elastic:your_password http://localhost:9200/_cluster/health?pretty能看到状态输出,说明HTTP层的认证已经生效。
3.2 让Kibana也穿上账号密码
Kibana和服务端通信用的不是elastic这个超级用户,而是专用的kibana_system用户。原因很简单:elastic用户拥有集群全权限,如果日志里出现它的使用记录,你很难区分到底是管理员在操作,还是某个组件出了幺蛾子。Kibana用最小权限的专用用户连接ES,是官方推荐做法。
配置在kibana.yml里:
elasticsearch.hosts: ["http://localhost:9200"] elasticsearch.username: "kibana_system" elasticsearch.password: "your_kibana_system_password"如果你启用了HTTPS,这里elasticsearch.hosts也要改写成https://,并且需要指定证书或关闭校验。测试环境可以设elasticsearch.ssl.verificationMode: none,生产环境千万别这么干。
改完重启Kibana,访问Kibana界面时会让你登录,用elastic或者其他有Kibana访问权限的账号登录就行。但这里经常有个坑:Kibana初次启动时需要往ES里写入Kibana的索引,如果kibana_system的密码没设置好,Kibana会一直停在Kibana server is not ready yet,而且日志里看不到明确原因。我的排查习惯是先看Kibana日志里有没有status为红色或黄色的服务依赖,然后再去ES日志里找有没有authentication failed。
3.3 其他组件如何接入:Logstash、Beats、Spring Boot
弄完Kibana,你可能会发现自己搭建的管道里还有Logstash和Filebeat。给ES加了认证之后,这些组件如果还按老配置连接,会直接报401。需要把它们各自的ES输出配置也改成带用户名密码。
比如Logstash的output段:
output { elasticsearch { hosts => ["http://localhost:9200"] user => "logstash_system" password => "your_logstash_system_password" index => "app-logs-%{+YYYY.MM.dd}" } }注意:如果Logstash需要向ES写入自定义索引、执行ILM、甚至创建索引模板,默认的logstash_system用户其实没有那么大的权限。我更倾向单独建一个专用用户,用后面的自定义角色授权,而不是直接把logstash_system的密码到处乱塞。
Spring Boot项目连接Elasticsearch时,配置方式也类似。拿spring-data-elasticsearch举例,在application.yml里:
spring: elasticsearch: uris: http://localhost:9200 username: springboot_es password: your_password如果你用的是RestHighLevelClient老写法,则在createClient()里设置CredentialsProvider,基于HTTP Basic认证的setDefaultCredentials就行。对于7.x和8.x的低版本RestClient,密码明文写在代码里这个问题,建议放到配置中心或环境变量里。
4. 角色与权限:让认证从“能进”变成“该进”
4.1 我为什么不建议所有人直接拿elastic账号干活
认证加好之后,很多团队就停在“能登录就行”这一步。这其实是最危险的状态:所有人共用elastic这个超级账号,出了问题完全查不到是谁干的。更现实的是,有些开发者误删索引,一个DELETE /logstash-*就把日志数据全清了,想恢复只能等手慢无的快照。
正确的姿势是:为不同场景创建独立用户,并通过角色控制权限。比如给业务日志索引的读写用户、给Kibana的访问用户、给运维的只读用户,分开管理。ES安全模型里,用户归属于一组角色,角色界定“能对哪些索引执行哪些操作”。
这里涉及两个核心API:
- 创建角色:
POST /_security/role/my_role - 创建用户:
POST /_security/user/my_user
我下面用一个实际案例来说:我们要给一个叫bigdata-app的应用创建一个用户,让它只能读写app-*索引,不能删除索引,不能访问集群管理API。
4.2 通过API创建角色和用户
先创建角色app_rw:
curl -u elastic:your_password -X POST "http://localhost:9200/_security/role/app_rw" -H 'Content-Type: application/json' -d' { "indices": [ { "names": ["app-*"], "privileges": ["read", "write"], "allow_restricted_indices": false } ], "cluster": ["monitor"] } '这里privileges数组里的read和write是最常用的。如果你要允许应用自己创建索引、管理别名,那得加上manage或create_index,否则应用想写入一个不存在的索引时会失败。我踩过这个坑:开发反馈“写不进数据”,结果查权限,角色里只有write,没有create_index。
接着创建用户,并把这角色挂上去:
curl -u elastic:your_password -X POST "http://localhost:9200/_security/user/bigdata_app" -H 'Content-Type: application/json' -d' { "password": "Passw0rd@2024", "roles": ["app_rw"], "full_name": "BigData Application", "email": "app@example.com", "enabled": true } '之后这个用户用HTTP Basic认证调用所有/app-*索引的读写接口都没问题,但删除索引这类高危操作会被ES拒绝。
4.3 在Kibana里维护权限
如果你觉得命令行写API麻烦,Kibana的“Stack Management -> Security -> Roles/Users”界面也提供了可视化管理。这里有一个额外好处:Kibana支持索引字段级别的权限控制,比如设定“只看message、timestamp字段”的只读角色,这在命令行API里配置会更复杂。
创建Kibana用户时需要注意一点:如果用户只负责投屏运维监控,让他访问Kibana的权限需要单独配置。Kibana有自己的一层权限(Kibana Spaces、Feature Privileges),和ES索引权限不冲突。最简单的方式是在ES侧创建用户时挂kibana_admin角色,但给普通开发更合理的往往是kibana_read_only。
从合规角度,我建议权限最小化:能只读就不要写,能指定索引就不要给全。集群权限里强烈不建议给*。如果你不了解某个privilege的具体含义,宁可不给,也不要先给后改。
5. 踩坑与排查:认证加完后最常见的五个翻车现场
5.1 节点间通信失败:transport握手无证书
加认证最常见的第一个坑是:配置完xpack.security.enabled: true,启动单节点没问题,但启动第二个节点时,第二个节点报错,日志里出现“not handshaking”或者“remote transport address is not active”。原因多半是Transport层的TLS没有正确配置,或者各节点用的证书不一致。
我当时的解决步骤是:
- 检查每个节点的
elasticsearch.yml,确认xpack.security.transport.ssl.enabled: true都开了。 - 确认
elastic-certificates.p12文件确实在每个节点的config目录下,并且权限是644。 - 用
bin/elasticsearch-certutil重新生成统一证书,复制到所有节点,重启集群。
如果不想在单机测试上折腾,可以先把xpack.security.transport.ssl.enabled设为false,但千万别把这习惯带进生产。没有TLS的认证就像在裸奔通道里喊暗号,密码都能被中间人截获。
5.2 不记得elastic密码,越急越乱
很多人第一次加认证都会遇到这个尴尬:密码忘了,Kibana连不上,ES启动报一堆401,管理员慌得想把安全问题关回去。正确的办法是用ES的elasticsearch-reset-password工具强制重置:
bin/elasticsearch-reset-password -u elastic -i交互式重置后,重新用新密码验证即可。注意,如果你之前已经把Kibana的kibana_system密码也忘了,同样可以用这个工具重置。
5.3 启用认证后,快照恢复和滚动升级变得“莫名其妙”
这个话题在热搜词里也出现了:elasticsearch 恢复数据。加认证之后,很多调用快照的脚本会挂,因为备份和恢复涉及集群权限。比如一个拥有all权限的用户不一定能执行POST /_snapshot/my_backup/repo_backup/_restore,需要manage_slm、monitor_snapshot或者具体仓库的权限。我的经验是:在角色配置里单独加:
"cluster": ["manage_slm", "monitor_snapshot"]并且把仓库路径的访问权限给到运行该操作的用户。否则你会看到“no handler for type [snapshot]”或者“is not authorized”的错误,日志提示并不直观,很容易误判为快照仓库路径挂了。
滚动升级时更要注意:老版本节点不支持新版本节点的认证握手,必须按官方跨版本兼容策略来。我遇到过一次从7.10升7.17,老节点没配TLS,新节点强制TLS,结果新老节点根本组不成一个集群。
5.4 ES端口暴露给外部,连接工具的适配问题
日常开发中,很多人喜欢用DBeaver、Navicat或者一些IDE插件连ES来查询数据。加认证之后,如果直接用这些工具连接,会出现各种连接超时或401。原因并不全是ES的问题,很多工具对HTTP Basic认证的字段要求不一样。比如DBeaver连接ES时,在“JDBC URL”后面如果要走ES的SQL接口,需要把用户名密码按标准方式填写,或者在自定义JDBC URL上加上auth参数。
更稳妥的方案是:不建议把ES 9200端口直接暴露给终端用户。数据库可视化工具要连,也建议通过Kibana或者一个带有访问控制的代理层。我在前面提到过,ES的端口应该是“后端服务”,不应该是“人人都能直连的东西”。给ES加认证只是第一步,加上访问隔离才是负责任的做法。
5.5 安全配置写错位置,改了不生效
ES的配置不像Spring Boot那么宽容,elasticsearch.yml里写错一个缩进和冒号,启动可能直接报错,甚至静默忽略某些配置项。我见过有人把xpack.security.enabled写在了yaml文件某行的错误缩进下,ES启动时认为这是一个未知的自定义配置,结果安全功能压根没开。
排查技巧是启动时观察日志里是否有以下一行:
Security is not enabled或者
X-Pack Security is enabled一旦看到日志明确输出,就知道生效没生效。另外,修改了elasticsearch.yml后,必须重启ES进程,动态的设置可以用APIcluster.put_settings,但安全相关配置绝大多数需要重启。
6. 认证之外:接入方适配和后续安全建议
6.1 从HTTP Basic到HTTPS:让认证在加密通道里跑
前面我已经提到过,HTTP Basic认证的账号密码在明文传输下是可以被截获的。如果你只开了认证,没有启用HTTPS,那你的密码实际上在网络上“裸奔”。ES的Security本身也支持HTTP层TLS,配置起来和Transport层大同小异,核心是把另外一个证书配到xpack.security.http.ssl相关项里。
测试环境为了省事,可以只给Kibana挂个HTTPS,ES和Kibana之间走内网HTTP,但在内网里也要注意别的服务段网络隔离。稳妥的做法是买个正规证书,或者用Let's Encrypt,实在不行再考虑自签。这里我特别提醒:自签证书使Java客户端连接时大概率会遇到PKIX path building failed,因为JVM的cacerts里没有你这个自签证书。需要把证书手动导入Java信任库:
keytool -importcert -file cert.crt -keystore $JAVA_HOME/lib/security/cacerts -alias es-cert这个步骤是很多Java开发连ES需要折腾半天的坑。
6.2 天然适配:Spring Boot的RestClient和API Key方式
跟Spring Boot集成的场景,我强烈建议不要只依赖用户名密码认证。ES支持创建API Key,可以给API Key设置过期时间、绑定指定角色,在程序里以Authorization: ApiKey <base64>的方式请求。相比密码,API Key更灵活:可以随时吊销、不用跟着密码改配置。
创建API Key的API:
curl -u elastic:your_password -X POST "http://localhost:9200/_security/api_key" -H 'Content-Type: application/json' -d' { "name": "springboot_app_key", "role_descriptors": { "app_rw_role": { "indices": [ { "names": ["app-*"], "privileges": ["read", "write"] } ] } } } '返回结果里会有api_key字段,程序端把这两段字符串处理成Base64(id:api_key)即可。很多现代框架都支持这个头。缺点是有些内部老旧HTTP客户端只支持Basic,那也只能继续用密码。
6.3 给运维留一个“只读”账号
认证配好后,团队成员总有几个需要查看集群健康、索引状态的人。不要让他们用elastic。运维人员可以给他们建一个read_only_admin角色:
{ "cluster": ["monitor"], "indices": [ { "names": ["*"], "privileges": ["read"] } ] }这样既能查看所有索引,又不能误删、误写Kibana或业务索引。如果你还需要他执行“滚动重启”之类的节点操作,那再单独加manage等权限,按需递增,不建议默认给满。
6.4 关于认证巡检:别让临时账号活到过年
我最后想聊一个运维习惯问题。很多团队加了认证以后就不再管了,这其实不对。建议至少每三个月做一次这几个操作:
- 用
elastic角色列表接口过一次所有用户,找出那些已经离职或长期没有登录的账号。 - 检查API Key列表,把超过90天没有活动的key吊销。
- 把
elastic超级账号改成强密码,并考虑把调用侧都换成专用账号。
Elasticsearch的Security设计本身是成熟的,真正出问题的往往是“只用不维护”。我自己见过太多临时加的用户和密码变成僵尸账号,到时候泄漏了都不知道是谁的。认证添加不是终点,权限治理才是真正让集群状态可控的开始。