给Elasticsearch集群添加用户密码
ES 7.x 默认裸奔,任何能连到 9200 端口的人都能执行 DELETE /index——因为在普及之初,ES 的设计假设是「运行在可信内网」。等到了发现需要认证时,往往不是「加个密码」那么简单:先得分清用户存在哪儿(本地 file realm 还是外部 LDAP/AD),两条路的配置方式完全不同,安全威胁模型也完全不同。
以下分两种情况说明:小集群用 file realm,对接企业身份体系用 external realm。在讲解配置之前,先理解几个关键概念。
版本说明
本文写于 2022-03,基于 Elasticsearch 7.x。
在 8.x 上的差异:安全特性默认开启,本文手动开启 xpack.security.enabled 并配置密码的步骤在 8.x 上已是默认行为,首次启动会自动生成密码;如需重置密码,命令 elasticsearch-reset-password 用法未变。
# 1. 区分 Realm 类型
Elasticsearch 支持多种用户域(realm):
- File realm(本地文件):用户存在
ES_PATH_CONF/users和roles.yml,适合小规模固定用户 - External realm(LDAP/AD/SAML/PKI):用户在外部系统,ES 只保存角色映射关系
不同 realm,配置方式完全不同——下文分两种情况说明。
# 2. 内置用户与密码初始化
ES 7.x 开启 xpack.security.enabled: true 后,会自动创建 5 个内置用户,用于集群内部组件通信和初始管理:
| 内置用户 | 用途 | 典型使用场景 |
|---|---|---|
elastic | 超级管理员,拥有 cluster 级别全部权限 | 仅用于初始配置和紧急恢复,日常不要分发 |
kibana_system | Kibana 连接 ES 时使用的身份 | Kibana 配置中的 elasticsearch.username |
logstash_system | Logstash 监控数据写入 | Logstash 配置中的 xpack.monitoring.elasticsearch.username |
beats_system | Beats 监控数据写入 | Beats 配置中的 setup.kibana.username |
apm_system | APM Server 数据写入 | APM Server 配置中的 output.elasticsearch.username |
# 密码初始化工具
7.x 使用 elasticsearch-setup-passwords(在 8.x 中已移除):
# 交互式设置全部内置用户密码
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive
# 批量自动设置(脚本用,输出到指定文件)
sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto --batch > /etc/es_passwords.txt
2
3
4
5
8.x 首启自动生成密码,存储在控制台输出或指定文件中。如需重置(该命令在 7.x 不可用):
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic
# 3. 在已有集群上开启安全特性
重要
开启安全特性不能滚动进行,必须全集群停机后统一改配置再一起启动(full cluster restart)。
xpack.security.enabled 与 transport TLS 是集群级的通信协议变更。开了安全的节点和没开的节点无法互相通信,滚动重启会让集群在中途分裂成两半互相认不出来。这是开安全特性最容易低估的运维代价,要专门排变更窗口。
# 3.1 完整的开启顺序
- 为 transport 层生成并分发证书到所有节点
- 所有节点同时改配置:启用
xpack.security.enabled和 transport TLS - 停整个集群:
systemctl stop elasticsearch或kill -TERM - 一起启动所有节点
- 设置内置用户密码:
elasticsearch-setup-passwords(7.x)或记下 8.x 自动生成的密码 - 逐个改客户端:Kibana、Logstash、Beats、业务方,全部加上认证
# 3.2 敏感值走 keystore,不要写进配置文件
密码、密钥等敏感值不要硬编码在 elasticsearch.yml 里,改用 elasticsearch-keystore:
# 添加敏感值
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
# 查看已有项
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore list
# 删除(如需轮换)
sudo /usr/share/elasticsearch/bin/elasticsearch-keystore remove xpack.security.transport.ssl.keystore.secure_password
2
3
4
5
6
7
8
配置文件里用 ${VAR} 占位,如:
xpack.security.transport.ssl.keystore.password: ${KESTORE_PASSWORD}
keystore 是每个节点本地的文件,加完要同步到所有节点,然后重启该节点生效。
# 3.3 客户端侧的变化
开启安全特性后,所有 curl 都要带 -u user:pass 或 API Key:
curl -u elastic:<密码> http://localhost:9200/_cluster/health
8.x 还默认开启 HTTP 层 TLS,需要额外带 --cacert 或 --insecure:
curl -u elastic:<密码> --cacert /etc/elasticsearch/certs/ca.crt https://localhost:9200/_cluster/health
忘了带认证的表现是 401 Unauthorized,不是连接失败或超时。排查时别往网络问题上钻。
# 4. File Realm(本地用户)
# 4.1 启用安全特性
# /etc/elasticsearch/elasticsearch.yml
xpack.security.enabled: true
2
重启 ES 生效:
sudo systemctl restart elasticsearch
# 4.2 使用 CLI 工具创建用户(不要直接编辑 users 文件)
# 创建用户并设置密码(交互式)
sudo /usr/share/elasticsearch/bin/elasticsearch-users useradd my_user -r my_role -p
# 非交互式(脚本用)
sudo /usr/share/elasticsearch/bin/elasticsearch-users useradd my_user -r my_role -p <密码>
2
3
4
5
-r 指定角色,多个角色用逗号分隔。该命令会同时修改 users 和 users_roles 两个文件,无需重启即生效。
# 4.3 定义角色权限
编辑 ES_PATH_CONF/roles.yml:
my_role:
cluster: ["monitor", "manage_index_templates"]
indices:
- names: ["logs-*"]
privileges: ["read", "write", "create_index"]
2
3
4
5
角色变更无需重启,但已登录用户需重新认证才生效。
# 4.4 验证
# 验证用户存在且角色正确
curl -u my_user:<密码> http://localhost:9200/_security/_authenticate
2
# 5. External Realm(LDAP/AD/SAML/PKI)
外部域用户不保存在 ES 本地,ES 只维护「外部身份 ↔ ES 角色」的映射关系。
# 5.1 启用并配置 realm
# elasticsearch.yml
xpack.security.authc.realms.ldap.ldap1:
order: 0
url: "ldaps://ldap.example.com:636"
bind_dn: "cn=admin,dc=example,dc=com"
bind_password: "${LDAP_PASSWORD}" # 用密钥库,不要明文
user_search:
base_dn: "ou=users,dc=example,dc=com"
filter: "(cn={0})"
group_search:
base_dn: "ou=groups,dc=example,dc=com"
files:
role_mapping: "ES_PATH_CONF/role_mapping.yml"
2
3
4
5
6
7
8
9
10
11
12
13
# 5.2 配置角色映射(role_mapping.yml)
# 将 LDAP 组映射到 ES 角色
my_role:
- "cn=ES_Admin,ou=groups,dc=example,dc=com"
- "cn=DevOps,ou=groups,dc=example,dc=com"
2
3
4
注意:role_mapping.yml 仅用于外部域。File realm 用户不应出现在此文件中。
# 5.3 重启生效
外部 realm 配置变更需要重启 ES:
sudo systemctl restart elasticsearch
# 6. 第三种方式:API Key
对于程序化访问(如 CI/CD 流水线、数据同步任务),与其分配长期账号密码,不如使用 API Key:
- 可设过期时间,减少泄露后长期有效带来的风险
- 可单独吊销,不影响其他凭证
- 权限精确到索引和动作,权限面更小
# 6.1 创建 API Key
curl -X POST "localhost:9200/_security/api_key" -u elastic:<密码> -H "Content-Type: application/json" -d '{
"name": "daily-backup-job",
"expiration": "30d",
"role_descriptors": {
"backup_role": {
"cluster": ["monitor"],
"indices": [
{
"names": ["logs-*"],
"privileges": ["read"]
}
]
}
}
}'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 6.2 使用 API Key
响应中包含 encoded 字段(id:api_key 的 base64),在请求头中使用:
curl -H "Authorization: ApiKey <base64_encoded_key>" http://localhost:9200/_cluster/health
# 6.3 吊销 API Key
# 通过 id 吊销
curl -X DELETE "localhost:9200/_security/api_key?id=<api_key_id>" -u elastic:<密码>
# 批量吊销(通过 name 或过期前吊销)
curl -X DELETE "localhost:9200/_security/api_key" -u elastic:<密码> -H "Content-Type: application/json" -d '{
"name": "daily-backup-job"
}'
2
3
4
5
6
7
API Key 的权限边界在创建时就已经确定,scopes 精确到索引、动作,与调用者身份解耦。这是服务账号升级后的安全形态。
# 7. 角色权限的最小化设计
ES 权限模型分为 cluster 级别和 indices 级别:
- Cluster 权限:创建索引、管理集群状态、查看监控等
- Indices 权限:索引的读、写、删除、查看 mapping 等
给只读查询方分配 read + view_index_metadata 即可,不要给 all;写入方要 write + create_index 但不需要 delete。
# 7.1 只读角色示例
# roles.yml - 只读角色
logs_reader:
cluster: ["monitor"]
indices:
- names: ["logs-*", "metrics-*"]
privileges: ["read", "view_index_metadata"]
2
3
4
5
6
monitor:允许查看集群健康状态(_cluster/health)read:查询文档view_index_metadata:获取索引 mapping 和 settings
# 7.2 写入角色示例
# roles.yml - 写入角色
logs_writer:
cluster: ["monitor", "manage_index_templates"]
indices:
- names: ["logs-*"]
privileges: ["write", "create_index", "auto_configure"]
2
3
4
5
6
write:写入文档、批量导入create_index:索引不存在时自动创建auto_configure:支持动态模板映射(ES 7.7+)- 不给
delete:防止误删索引或文档
# 7.3 管理角色示例
# roles.yml - 管理角色(供运维人员)
logs_admin:
cluster: ["monitor", "manage_ilm", "manage_index_templates", "cluster:admin/ingest/pipeline/put"]
indices:
- names: ["logs-*", ".ilm-*"]
privileges: ["all"]
2
3
4
5
6
# 8. 为什么 transport.ssl 是必需的
重要
开启 xpack.security.enabled: true 后,必须同时配置 transport 层 TLS,否则认证形同虚设。
ES 节点间通过 transport 端口(默认 9300)通信。如果这个通道不加密且不要求证书认证,任何能连上 9300 的进程都能伪装成集群节点加入,继承集群的查询权限,甚至伪装成 master 下发分片分配指令。这就是「节点伪装(node impersonation)」攻击。
因此,安全加固的正常顺序是:
- 为 transport 层生成 CA 和节点证书(创建密钥库
elasticsearch.keystore) - 在 elasticsearch.yml 中启用 transport 层加密和双向证书认证,要求所有节点信任同一 CA
- 在 elasticsearch.yml 中启用 http 层 TLS(对外 9200 端口)
- 最后 才启用
xpack.security.enabled: true,用内置工具初始化账号密码
原文件里那一堆 keystore、truststore、certificate_authorities 配置不是可选的安全增强,而是让认证真正生效的前提——没有它们,节点可被任意仿冒,账号密码等于没用。
# 9. 常见错误
| 错误 | 原因 | 修复 |
|---|---|---|
No user found | 用 role_mapping.yml 给 file realm 用户配角色 | File realm 直接用 elasticsearch-users 分配角色,不要改 role_mapping.yml |
Incorrect password | 直接 nano 编辑 users 文件后格式错误 | 始终用 elasticsearch-users CLI 工具操作 |
| 用户无权限 | 角色权限配置在 roles.yml 但用户未绑定 | 检查 users_roles 文件或 CLI 输出中的角色列表 |
# 10. 验证清单
# 1. 安全特性已启用
GET _cluster/settings?include_defaults=true&filter_path=defaults.xpack.security.enabled
# 2. 当前生效的 realm 列表
GET _security/_authenticate
# 3. 特定用户权限详情
GET _security/user/my_user
# 4. 角色权限定义
GET _security/role/my_role
2
3
4
5
6
7
8
9
10
11