ElasticSearch索引和搜索的安全策略
2022-04-136.6k 阅读
ElasticSearch 索引和搜索的安全策略
ElasticSearch 安全概述
ElasticSearch 是一个分布式的开源搜索和分析引擎,广泛应用于各种数据搜索和分析场景。随着数据的重要性日益增加,ElasticSearch 的安全策略至关重要。它涉及到保护索引数据不被未授权访问、修改或删除,确保搜索功能仅对合法用户可用。
ElasticSearch 的安全威胁主要包括以下几类:
- 未授权访问:恶意用户可能尝试直接访问 ElasticSearch 集群的 API 端点,获取敏感数据。例如,通过直接调用搜索 API 绕过应用层的权限控制。
- 数据篡改:攻击者可能尝试修改索引中的数据,破坏数据的完整性。比如,篡改商品价格等关键信息。
- 拒绝服务攻击(DoS):大量的请求可能导致 ElasticSearch 集群资源耗尽,无法正常提供服务。例如,通过发送大量无效的搜索请求占用服务器资源。
身份验证策略
内置身份验证
ElasticSearch 从 5.0 版本开始引入了内置的身份验证机制。这包括使用用户名和密码进行基本身份验证。
- 启用内置身份验证
- 首先,需要在
elasticsearch.yml
配置文件中启用 X-Pack 安全模块(X-Pack 包含了身份验证、授权等安全功能)。确保xpack.security.enabled: true
。 - 然后,使用
elasticsearch-setup-passwords
工具来设置内置用户(如elastic
、kibana
等)的密码。例如,在命令行中执行:
这个命令会提示用户设置各个内置用户的密码。bin/elasticsearch -setup -passwords interactive
- 首先,需要在
- 使用基本身份验证进行 API 调用
- 以 Python 的
elasticsearch
客户端为例,使用基本身份验证进行连接:
from elasticsearch import Elasticsearch es = Elasticsearch( ['localhost:9200'], http_auth=('elastic', 'your_password'), scheme="http", port=9200, )
- 在 Java 中,使用
RestHighLevelClient
进行基本身份验证连接:
import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; public class ElasticsearchClient { public static void main(String[] args) { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials("elastic", "your_password") ); RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http")) .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)) ); // 使用 client 进行操作 try { client.close(); } catch (IOException e) { e.printStackTrace(); } } }
- 以 Python 的
外部身份验证集成
- LDAP 集成
- LDAP(轻量级目录访问协议)是一种常用的企业级身份验证方式。要将 ElasticSearch 与 LDAP 集成,首先需要在
elasticsearch.yml
中配置 LDAP 相关参数。 - 示例配置如下:
xpack.security.authc: ldap1: order: 0 type: ldap realm: ldap1 url: ldap://your_ldap_server:389 bind_dn: cn=admin,dc=example,dc=com bind_password: your_bind_password user_search: base_dn: ou=users,dc=example,dc=com filter: (uid={0}) group_search: base_dn: ou=groups,dc=example,dc=com filter: (member={0})
- 这里
url
是 LDAP 服务器地址,bind_dn
和bind_password
用于 ElasticSearch 与 LDAP 服务器进行绑定。user_search
和group_search
分别配置了用户和组的搜索路径和过滤条件。 - 在客户端进行连接时,用户可以使用 LDAP 中的用户名和密码进行身份验证,ElasticSearch 会将认证请求转发到 LDAP 服务器进行验证。
- LDAP(轻量级目录访问协议)是一种常用的企业级身份验证方式。要将 ElasticSearch 与 LDAP 集成,首先需要在
- OAuth 2.0 集成
- OAuth 2.0 是一种广泛使用的授权框架,常用于 Web 应用的身份验证。要集成 OAuth 2.0,需要在 ElasticSearch 中配置 OAuth 2.0 提供方的相关信息。
- 例如,使用 Okta 作为 OAuth 2.0 提供方,在
elasticsearch.yml
中的配置如下:
xpack.security.authc: oauth2: type: oauth2 order: 0 realm: oauth2 client_id: your_client_id client_secret: your_client_secret token_endpoint: https://your_okta_domain/oauth2/default/v1/token userinfo_endpoint: https://your_okta_domain/oauth2/default/v1/userinfo jwks_uri: https://your_okta_domain/oauth2/default/v1/keys claims: username: sub
- 这里
client_id
和client_secret
是在 Okta 中创建应用时生成的,token_endpoint
、userinfo_endpoint
和jwks_uri
是 Okta 的相关端点。claims
中指定了从 OAuth 2.0 响应中提取用户名的字段。 - 客户端通过 OAuth 2.0 流程获取令牌后,在与 ElasticSearch 进行交互时,将令牌包含在请求头中,ElasticSearch 会验证令牌的有效性并进行身份验证。
授权策略
基于角色的访问控制(RBAC)
- 角色定义
- 在 ElasticSearch 中,可以定义不同的角色来控制用户对索引和搜索功能的访问权限。角色可以通过 ElasticSearch 的 API 进行创建和管理。
- 使用
PUT _security/role/{role_name}
API 来创建角色。例如,创建一个名为read_only_role
的角色,该角色只允许对特定索引进行只读访问:
PUT _security/role/read_only_role { "cluster": [ "cluster:monitor/nodes/stats" ], "indices": [ { "names": [ "index1", "index2" ], "privileges": [ "read" ] } ] }
- 这里
cluster
部分定义了对集群的权限,indices
部分定义了对特定索引的权限。privileges
中的read
表示只读权限。
- 角色分配
- 可以将角色分配给用户。使用
PUT _security/user/{username}
API 来分配角色。例如,将read_only_role
分配给用户user1
:
PUT _security/user/user1 { "password": "user1_password", "roles": [ "read_only_role" ] }
- 这样,用户
user1
就只能对index1
和index2
进行只读操作。
- 可以将角色分配给用户。使用
基于文档级别的访问控制
- 文档级权限插件
- 对于一些需要更细粒度权限控制的场景,比如不同用户只能访问自己创建的文档,可以使用 ElasticSearch 的文档级权限插件。例如,
Search Guard
插件提供了文档级别的访问控制功能。 - 首先,需要安装
Search Guard
插件。安装完成后,在配置文件中定义文档级别的权限规则。 - 假设索引中有一个
user_id
字段来标识文档的所有者,在sg_security.yml
配置文件中可以定义如下规则:
searchguard: dynamic: doclevelroles: - name: user_document_access description: "Allow users to access their own documents" rules: - and: - match: field: user_id value: "{{user_name}}"
- 然后,将这个
doclevelroles
角色分配给用户。例如,在sg_internal_users.yml
中:
user1: hash: $2a$12$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx roles: - user_document_access
- 这样,用户
user1
就只能访问user_id
为user1
的文档。
- 对于一些需要更细粒度权限控制的场景,比如不同用户只能访问自己创建的文档,可以使用 ElasticSearch 的文档级权限插件。例如,
- 使用脚本实现文档级权限
- ElasticSearch 也支持使用脚本实现文档级别的权限控制。例如,通过在搜索请求中使用脚本过滤来实现文档级权限。
- 假设索引中有一个
created_by
字段,以下是一个使用 Python 的elasticsearch
客户端进行搜索时,通过脚本过滤只返回当前用户创建的文档的示例:
from elasticsearch import Elasticsearch es = Elasticsearch( ['localhost:9200'], http_auth=('user1', 'user1_password'), scheme="http", port=9200, ) search_body = { "query": { "script": { "script": { "source": "doc['created_by'].value == params.current_user", "params": { "current_user": "user1" } } } } } result = es.search(index='your_index', body=search_body) print(result)
- 这里通过脚本判断
created_by
字段的值是否与当前用户相同,从而实现文档级别的权限控制。
数据加密策略
传输层加密
- SSL/TLS 配置
- ElasticSearch 支持使用 SSL/TLS 对传输层数据进行加密,以防止数据在网络传输过程中被窃取或篡改。
- 首先,需要生成 SSL/TLS 证书。可以使用 OpenSSL 工具生成自签名证书:
openssl req -new -x509 -days 365 -nodes -out elasticsearch.pem -keyout elasticsearch.key -subj "/CN=localhost"
- 然后,在
elasticsearch.yml
中配置 SSL/TLS 相关参数:
xpack.security.transport.ssl.enabled: true xpack.security.transport.ssl.verification_mode: certificate xpack.security.transport.ssl.keystore.path: elasticsearch.p12 xpack.security.transport.ssl.truststore.path: elasticsearch.p12
- 这里
xpack.security.transport.ssl.enabled
启用了传输层 SSL/TLS 加密,verification_mode
设置为certificate
表示验证对方的证书,keystore.path
和truststore.path
指向生成的证书文件(这里假设将证书转换为 PKCS12 格式)。 - 在客户端连接时,也需要配置相应的 SSL/TLS 证书。以 Python 的
elasticsearch
客户端为例:
from elasticsearch import Elasticsearch es = Elasticsearch( ['localhost:9200'], http_auth=('elastic', 'your_password'), scheme="https", port=9200, ssl_show_warn=False, ca_certs='path/to/your/certificate' )
- 在 Java 中,使用
RestHighLevelClient
配置 SSL/TLS:
import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.ssl.SSLContexts; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import javax.net.ssl.SSLContext; import java.io.File; import java.io.FileInputStream; import java.security.KeyStore; public class ElasticsearchClient { public static void main(String[] args) { try { KeyStore truststore = KeyStore.getInstance("PKCS12"); File truststoreFile = new File("path/to/your/certificate.p12"); try (FileInputStream fis = new FileInputStream(truststoreFile)) { truststore.load(fis, "your_password".toCharArray()); } SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(truststore, null) .build(); final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials("elastic", "your_password") ); RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "https")) .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder .setDefaultCredentialsProvider(credentialsProvider) .setSSLContext(sslContext)) ); // 使用 client 进行操作 try { client.close(); } catch (IOException e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } } }
存储层加密
- 磁盘加密
- ElasticSearch 支持对存储在磁盘上的数据进行加密,以防止数据在物理存储设备丢失或被盗时被轻易获取。
- 从 ElasticSearch 7.1 版本开始,引入了基于磁盘的加密功能。可以通过设置
xpack.security.storage.encryption.keystore.seed
来启用存储层加密。 - 首先,生成一个加密种子:
bin/elasticsearch -keystore generate -seed
- 然后,将生成的种子添加到
elasticsearch.keystore
中:
bin/elasticsearch -keystore add xpack.security.storage.encryption.keystore.seed
- 重启 ElasticSearch 集群后,数据在写入磁盘时会自动进行加密。需要注意的是,存储层加密需要在集群初始化时进行配置,一旦数据已经写入磁盘,再启用存储层加密可能需要重新索引数据。
网络安全策略
防火墙配置
- 限制 ElasticSearch 端口访问
- ElasticSearch 默认使用 9200 端口进行 HTTP 通信,9300 端口进行节点间通信。在服务器上配置防火墙,只允许授权的 IP 地址访问这些端口。
- 以 Linux 的
iptables
为例,允许本地访问和特定远程 IP 地址访问 ElasticSearch 的 HTTP 端口(9200):
iptables -A INPUT -i lo -p tcp --dport 9200 -j ACCEPT iptables -A INPUT -p tcp -s your_allowed_ip -m tcp --dport 9200 -j ACCEPT iptables -A INPUT -p tcp --dport 9200 -j DROP
- 这里
-A INPUT
表示在输入链中添加规则,-i lo
表示本地接口,-s your_allowed_ip
表示允许的远程 IP 地址,-j ACCEPT
表示接受连接,-j DROP
表示拒绝其他连接。
- 隔离 ElasticSearch 集群
- 可以将 ElasticSearch 集群部署在独立的子网中,通过防火墙与其他网络进行隔离。这样可以减少外部攻击面,提高安全性。例如,使用虚拟专用网络(VPN)或软件定义网络(SDN)技术来创建独立的网络区域。在这个独立子网中,只允许特定的服务器(如应用服务器)与 ElasticSearch 集群进行通信。
安全代理设置
- 使用反向代理
- 可以在 ElasticSearch 前端部署反向代理服务器,如 Nginx 或 Apache。反向代理可以起到隐藏 ElasticSearch 真实 IP 地址、缓存请求、进行访问控制等作用。
- 以下是一个 Nginx 作为反向代理配置的示例:
server { listen 80; server_name your_domain.com; location / { proxy_pass http://localhost:9200; proxy_set_header Host $host; proxy_set_header X - Real - IP $remote_addr; proxy_set_header X - Forwarded - For $proxy_add_x_forwarded_for; proxy_set_header X - Forwarded - Proto $scheme; } }
- 这里
proxy_pass
将请求转发到本地的 ElasticSearch 服务器(假设 ElasticSearch 运行在localhost:9200
),同时设置了一些请求头信息。可以在 Nginx 中进一步配置身份验证、访问控制等功能,例如使用auth_basic
进行基本身份验证。
- 负载均衡与安全
- 在高可用部署中,通常会使用负载均衡器来分配请求到多个 ElasticSearch 节点。除了实现负载均衡功能外,负载均衡器也可以增强安全性。例如,一些负载均衡器支持 SSL/TLS 卸载,即在负载均衡器上终止 SSL/TLS 连接,然后以明文形式将请求转发到 ElasticSearch 节点。这样可以减少 ElasticSearch 节点的负载,同时也可以在负载均衡器上进行更集中的证书管理。另外,负载均衡器可以设置请求速率限制等功能,防止恶意的高流量攻击。
安全监控与审计
安全日志记录
- ElasticSearch 日志配置
- ElasticSearch 本身提供了详细的日志记录功能,包括身份验证、授权等安全相关的操作日志。可以通过配置
logging.yml
文件来调整日志级别和输出路径。 - 例如,要启用详细的安全日志记录,在
logging.yml
中设置:
logger.org.elasticsearch.xpack.security: DEBUG
- 这样,ElasticSearch 会记录更详细的安全相关操作,如身份验证尝试、授权决策等。日志文件默认存储在
logs
目录下,可以通过path.logs
在elasticsearch.yml
中进行自定义。
- ElasticSearch 本身提供了详细的日志记录功能,包括身份验证、授权等安全相关的操作日志。可以通过配置
- 日志分析
- 可以使用 ElasticSearch 自身或其他日志分析工具(如 Kibana、Logstash 等)对安全日志进行分析。例如,在 Kibana 中,可以创建可视化图表来展示身份验证失败次数、特定用户的操作频率等信息。通过分析这些日志,可以及时发现潜在的安全威胁,如暴力破解密码尝试等。
审计 API
- 审计日志记录
- ElasticSearch 提供了审计 API,可以记录所有对集群的操作,包括索引创建、删除、搜索等。通过启用审计功能,在
elasticsearch.yml
中配置:
xpack.security.audit.enabled: true xpack.security.audit.destination: internal_elasticsearch
- 这里
xpack.security.audit.enabled
启用了审计功能,destination
设置为internal_elasticsearch
表示将审计日志记录到 ElasticSearch 自身的索引中。也可以将审计日志发送到外部系统,如 Kafka 等。
- ElasticSearch 提供了审计 API,可以记录所有对集群的操作,包括索引创建、删除、搜索等。通过启用审计功能,在
- 查询审计日志
- 可以使用 ElasticSearch 的搜索 API 来查询审计日志。例如,要查询所有用户的搜索操作记录,可以执行以下搜索请求:
GET _xpack/audit/_search { "query": { "match": { "event.type": "search" } } }
- 这样可以获取所有搜索操作的审计日志记录,包括执行搜索的用户、搜索的索引、搜索条件等信息。通过分析这些审计日志,可以对用户的操作进行追溯和安全审计。
通过综合实施以上 ElasticSearch 索引和搜索的安全策略,可以有效地保护 ElasticSearch 集群的数据安全和功能可用性,降低安全风险。在实际应用中,需要根据具体的业务需求和安全要求进行灵活配置和调整。