MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

ElasticSearch索引和搜索的安全策略

2022-04-136.6k 阅读

ElasticSearch 索引和搜索的安全策略

ElasticSearch 安全概述

ElasticSearch 是一个分布式的开源搜索和分析引擎,广泛应用于各种数据搜索和分析场景。随着数据的重要性日益增加,ElasticSearch 的安全策略至关重要。它涉及到保护索引数据不被未授权访问、修改或删除,确保搜索功能仅对合法用户可用。

ElasticSearch 的安全威胁主要包括以下几类:

  1. 未授权访问:恶意用户可能尝试直接访问 ElasticSearch 集群的 API 端点,获取敏感数据。例如,通过直接调用搜索 API 绕过应用层的权限控制。
  2. 数据篡改:攻击者可能尝试修改索引中的数据,破坏数据的完整性。比如,篡改商品价格等关键信息。
  3. 拒绝服务攻击(DoS):大量的请求可能导致 ElasticSearch 集群资源耗尽,无法正常提供服务。例如,通过发送大量无效的搜索请求占用服务器资源。

身份验证策略

内置身份验证

ElasticSearch 从 5.0 版本开始引入了内置的身份验证机制。这包括使用用户名和密码进行基本身份验证。

  1. 启用内置身份验证
    • 首先,需要在 elasticsearch.yml 配置文件中启用 X-Pack 安全模块(X-Pack 包含了身份验证、授权等安全功能)。确保 xpack.security.enabled: true
    • 然后,使用 elasticsearch-setup-passwords 工具来设置内置用户(如 elastickibana 等)的密码。例如,在命令行中执行:
    bin/elasticsearch -setup -passwords interactive
    
    这个命令会提示用户设置各个内置用户的密码。
  2. 使用基本身份验证进行 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();
            }
        }
    }
    

外部身份验证集成

  1. 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_dnbind_password 用于 ElasticSearch 与 LDAP 服务器进行绑定。user_searchgroup_search 分别配置了用户和组的搜索路径和过滤条件。
    • 在客户端进行连接时,用户可以使用 LDAP 中的用户名和密码进行身份验证,ElasticSearch 会将认证请求转发到 LDAP 服务器进行验证。
  2. 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_idclient_secret 是在 Okta 中创建应用时生成的,token_endpointuserinfo_endpointjwks_uri 是 Okta 的相关端点。claims 中指定了从 OAuth 2.0 响应中提取用户名的字段。
    • 客户端通过 OAuth 2.0 流程获取令牌后,在与 ElasticSearch 进行交互时,将令牌包含在请求头中,ElasticSearch 会验证令牌的有效性并进行身份验证。

授权策略

基于角色的访问控制(RBAC)

  1. 角色定义
    • 在 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 表示只读权限。
  2. 角色分配
    • 可以将角色分配给用户。使用 PUT _security/user/{username} API 来分配角色。例如,将 read_only_role 分配给用户 user1
    PUT _security/user/user1
    {
        "password": "user1_password",
        "roles": [
            "read_only_role"
        ]
    }
    
    • 这样,用户 user1 就只能对 index1index2 进行只读操作。

基于文档级别的访问控制

  1. 文档级权限插件
    • 对于一些需要更细粒度权限控制的场景,比如不同用户只能访问自己创建的文档,可以使用 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_iduser1 的文档。
  2. 使用脚本实现文档级权限
    • 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 字段的值是否与当前用户相同,从而实现文档级别的权限控制。

数据加密策略

传输层加密

  1. 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.pathtruststore.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();
            }
        }
    }
    

存储层加密

  1. 磁盘加密
    • 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 集群后,数据在写入磁盘时会自动进行加密。需要注意的是,存储层加密需要在集群初始化时进行配置,一旦数据已经写入磁盘,再启用存储层加密可能需要重新索引数据。

网络安全策略

防火墙配置

  1. 限制 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 表示拒绝其他连接。
  2. 隔离 ElasticSearch 集群
    • 可以将 ElasticSearch 集群部署在独立的子网中,通过防火墙与其他网络进行隔离。这样可以减少外部攻击面,提高安全性。例如,使用虚拟专用网络(VPN)或软件定义网络(SDN)技术来创建独立的网络区域。在这个独立子网中,只允许特定的服务器(如应用服务器)与 ElasticSearch 集群进行通信。

安全代理设置

  1. 使用反向代理
    • 可以在 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 进行基本身份验证。
  2. 负载均衡与安全
    • 在高可用部署中,通常会使用负载均衡器来分配请求到多个 ElasticSearch 节点。除了实现负载均衡功能外,负载均衡器也可以增强安全性。例如,一些负载均衡器支持 SSL/TLS 卸载,即在负载均衡器上终止 SSL/TLS 连接,然后以明文形式将请求转发到 ElasticSearch 节点。这样可以减少 ElasticSearch 节点的负载,同时也可以在负载均衡器上进行更集中的证书管理。另外,负载均衡器可以设置请求速率限制等功能,防止恶意的高流量攻击。

安全监控与审计

安全日志记录

  1. ElasticSearch 日志配置
    • ElasticSearch 本身提供了详细的日志记录功能,包括身份验证、授权等安全相关的操作日志。可以通过配置 logging.yml 文件来调整日志级别和输出路径。
    • 例如,要启用详细的安全日志记录,在 logging.yml 中设置:
    logger.org.elasticsearch.xpack.security: DEBUG
    
    • 这样,ElasticSearch 会记录更详细的安全相关操作,如身份验证尝试、授权决策等。日志文件默认存储在 logs 目录下,可以通过 path.logselasticsearch.yml 中进行自定义。
  2. 日志分析
    • 可以使用 ElasticSearch 自身或其他日志分析工具(如 Kibana、Logstash 等)对安全日志进行分析。例如,在 Kibana 中,可以创建可视化图表来展示身份验证失败次数、特定用户的操作频率等信息。通过分析这些日志,可以及时发现潜在的安全威胁,如暴力破解密码尝试等。

审计 API

  1. 审计日志记录
    • ElasticSearch 提供了审计 API,可以记录所有对集群的操作,包括索引创建、删除、搜索等。通过启用审计功能,在 elasticsearch.yml 中配置:
    xpack.security.audit.enabled: true
    xpack.security.audit.destination: internal_elasticsearch
    
    • 这里 xpack.security.audit.enabled 启用了审计功能,destination 设置为 internal_elasticsearch 表示将审计日志记录到 ElasticSearch 自身的索引中。也可以将审计日志发送到外部系统,如 Kafka 等。
  2. 查询审计日志
    • 可以使用 ElasticSearch 的搜索 API 来查询审计日志。例如,要查询所有用户的搜索操作记录,可以执行以下搜索请求:
    GET _xpack/audit/_search
    {
        "query": {
            "match": {
                "event.type": "search"
            }
        }
    }
    
    • 这样可以获取所有搜索操作的审计日志记录,包括执行搜索的用户、搜索的索引、搜索条件等信息。通过分析这些审计日志,可以对用户的操作进行追溯和安全审计。

通过综合实施以上 ElasticSearch 索引和搜索的安全策略,可以有效地保护 ElasticSearch 集群的数据安全和功能可用性,降低安全风险。在实际应用中,需要根据具体的业务需求和安全要求进行灵活配置和调整。