亚马逊AWS官方博客

Open Distro for Elasticsearch 的 LDAP 集成

Open Distro for Elasticsearch 的安全插件自带即开即用的身份验证和访问控制功能。在前几篇文章中,我们展示了如何在 Open Distro for Elasticsearch 中更改管理员密码以及如何将自己的 SSL 证书添加到 Open Distro for Elasticsearch

使用安全插件的关键步骤之一是确定身份验证后端。该插件包含一个内部用户数据库,但是许多人员更喜欢使用现有的身份验证后端(例如 LDAP 服务器)或者两者的某种组合。在本文中,我们将讨论如何将安全插件与 LDAP 或 Active Directory 集成,并配置后端用户角色与 Elasticsearch 安全角色之间的映射,以提供粒度访问控制。

身份验证和授权模块的主配置文件为 plugins/opendistro_security/securityconfig/config.yml。该文件定义安全插件如何检索用户凭证,如何验证这些凭证以及如何从后端系统获取其他用户角色。有关此配置文件结构的详细信息,请参阅 Open Distro for Elasticsearch 文档

在 LDAP/AD 中创建用户并分配到组

我使用 Active Directory 作为 LDAP 服务器。使用“Active Directory 用户和计算机”管理工具,我创建了三个用户(esuser1、esuser2 和 esuser3)以及两个组(ESAdmins 和 ES-Read-Grp)。我已使 esuser1 成为 ESAdmins 组的成员,esuser2 成为 ES-Read-Grp 组的成员,而 esuser3 不属于任何组。

 

针对 Open Distro for Elasticsearch 的 Microsoft Active Directory 用户配置

对安全插件配置应用更改

安全插件将其配置(包括用户、角色和权限)存储在 Elasticsearch 集群 (.opendistro_security) 的索引中。将这些设置存储在索引中,可以在更改设置后无需重新启动集群,也不需要将配置文件放在任何节点上。

但是,更改 plugins/opendistro_security/securityconfig 中的任何配置文件后,必须运行 plugins/opendistro_security/tools/securityadmin.sh,将这些新设置加载到索引。此脚本通过管理员 TLS 证书(采用 .pem.jks.p12.pfx 格式)针对集群自我识别。

如果 .opendistro_security 索引已初始化,您也可以使用 Kibana 更改用户、角色和权限。但是,您需要至少运行一次 securityadmin.sh 来初始化索引并配置身份验证和授权方法。

有关如何使用 securityadmin.sh 的更多详细信息,请参阅 Open Distro for Elasticsearch 文档

在安全插件中配置 LDAP 详细信息

LDAP 可用于身份验证和授权,因此可以在配置的 authcauthz 部分中使用。authc 部分用于配置身份验证,这意味着可用于检查用户是否输入了正确的凭证。authz 用于授权,定义如何检索和映射经过身份验证的用户的角色。

对于新设置,可以使用 plugins/opendistro_security/securityconfig/config.yml 更新 LDAP 配置详细信息。在既有的设置中,确保从正在运行的集群 (securityadmin.sh -r…) 检索当前安全插件配置并使用这些文件避免丢失任何更改。

身份验证

将以下行添加到 config.yml 文件的 authc 部分以启用 LDAP 身份验证:

  ... 
    authc:
      ldap:
        http_enabled: true
        transport_enabled: false
        order: 1
        http_authenticator:
          type: "basic"
          challenge: false
        authentication_backend:
          type: "ldap"
          config:
   ...

在 config 部分,我们将配置 LDAP 连接设置:主机名和端口 (hosts:)、BindDN (bind_dn:) 和密码 (password)。以下是此部分的副本 – 将 "<< password >>" 替换为绑定用户的实际纯文本密码。

          config:
            enable_ssl: false
            enable_start_tls: false
            enable_ssl_client_auth: false
            verify_hostnames: true
            hosts:
                - "ad.example.com:389"
            bind_dn: "cn=esuser1,OU=Users,OU=AD,dc=ad,dc=example,dc=com"
            password: "<< password >>"
            userbase: "OU=Users,OU=AD,dc=ad,dc=example,dc=com"
            usersearch: "(sAMAccountName={0})"
            username_attribute: "cn"

身份验证的运作方式为:对 LDAP 树的用户子树发出包含用户名的 LDAP 查询。

安全插件首先接受已配置的 LDAP 查询,并将占位符 {0} 替换为用户凭证中的用户名。

usersearch: "(sAMAccountName={0})"

然后对用户子树发出此查询。目前,将搜索配置的 userbase 下面的整个子树:

userbase: "OU=Users,OU=AD,dc=ad,dc=example,dc=com"

如果查询成功,安全插件将从 LDAP 条目检索用户名。您可以指定安全插件应该使用 LDAP 条目中的哪个属性作为用户名:

username_attribute: "cn"

如果此项未设置或为 null,则使用 LDAP 条目的可分辨名称 (DN)。我已设置为“CN”。

授权

将以下行添加到 config.yml 文件的 authz 部分以启用 LDAP 授权:

...
    authz:
      roles_from_myldap:
        http_enabled: true
        transport_enabled: false
        authorization_backend:
          type: "ldap"
          config:
...

授权是从 LDAP 服务器检索经过身份验证的用户的后端角色的过程。此服务器通常是用于身份验证的同一服务器,但也可以使用不同的服务器。惟一的要求是所需的用户确实存在于 LDAP 服务器上。由于安全插件始终检查用户是否存在于 LDAP 服务器上,因此您需要在 authz 部分配置 userbaseusersearchusername_attribute。授权的运作方式与身份验证类似。安全插件对 LDAP 树的角色子树发出包含用户名的 LDAP 查询。以下是此部分的副本:

        config:
            enable_ssl: false
            enable_start_tls: false
            enable_ssl_client_auth: false
            verify_hostnames: true
            hosts:
            - "ad.example.com:389"
            bind_dn: "cn=esuser1,OU=Users,OU=AD,dc=ad,dc=example,dc=com"
            password: "Test1234"
            userbase: "OU=Users,OU=AD,dc=ad,dc=example,dc=com"
            usersearch: "(uid={0})"
            rolebase: "OU=Groups,OU=AD,dc=ad,dc=example,dc=com"
            rolesearch: "(member={0})"
            userrolename: "memberOf"
            rolename: "cn"

安全插件首先接受用于获取角色的 LDAP 查询(“rolesearch”),并替换在查询中发现的任何变量。例如,对于标准的 Active Directory 安装,您将使用以下角色搜索。此处的 {0} 将替换为用户的 DN:

rolesearch: '(member={0})'

然后,安全插件对配置的角色子树发出替换的查询。将搜索 rolebase 下面的整个子树。

rolebase: 'ou=groups,dc=example,dc=com'

获取所有角色之后,安全插件将从角色条目的可配置属性中提取最终的角色名称:

rolename: 'cn'

如果未设置此项,将使用角色条目的 DN。

更新安全插件配置

现在,运行 securityadmin.sh 以更新安全插件配置,如下所示:

$ /usr/share/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh \
    -cacert /etc/elasticsearch/root-ca.pem \
-cert /etc/elasticsearch/kirk.pem \ 
-key /etc/elasticsearch/kirk-key.pem \
-h <Cluster-IP-or-FQDN> \
-f <Path-to-config>/config.yml -t config

这里,我指定了根 CA 证书 (-cacert)、管理员证书 (-cert) 和管理员私有密钥 (-key) 文件的路径。管理员证书的 DN 需要在 elasticsearch.yml 中的 opendistro_security.authcz.admin_dn 下配置。我通过明确指定配置文件位置,限制为仅对此配置文件进行此更新。

创建 Elasticsearch 安全角色

Open Distro for Elasticsearch 的安全插件内置默认操作组。我们将创建新角色,并使用这些默认操作组分配集群级和索引级权限。有关创建组合默认操作组和单一权限的新自定义操作组以及如何将文档和字段级安全性添加到角色定义中的详细信息,请参阅文档。

Kibana

使用 Kibana 作为“管理员”用户,我创建了两个自定义角色,一个具有完全的读/写权限,另一个仅具有只读权限。

选择“Security”并单击 Roles
Open Distro for Elasticsearch 安全插件的 Kibana 主面板

单击 + 按钮,添加新角色:

显示 Open Distro for Elasticsearch 的安全插件角色管理页面

输入新的角色名称:

用于为 Open Distro for Elasticsearch 中的新后端角色设置角色名称的 Kibana 面板

选择 Cluster Permissions,然后在“Action Groups”和/或“Single Permissions”下添加值:

在 Open Distro for Elasticsearch 安全插件中添加新后端角色的权限

选择 Index Permissions,然后添加索引模式(支持通配符)或选择一个索引。

对于每个索引模式,在“Action Groups”和/或“Single Permissions”下添加值。

完成后,单击 Save Role Definition

为 Open Distro for Elasticsearch 中的新后端角色设置索引权限

将 LDAP 后端角色映射到 Elasticsearch 安全角色

可以将 Elasticsearch 角色映射到用户名、后端角色和/或主机。后端角色确定为身份验证和授权过程的一部分,可以来自内部用户配置、LDAP 或 JSON Web Token。我们将角色映射到 LDAP 组名。

在 Kibana 中,选择 Security 并单击 Role Mappings

 

Open Distro for Elasticsearch 安全插件的主 Kibana 面板,显示了角色映射按钮的位置

单击 + 按钮,添加新角色映射:

Open Distro for Elasticsearch 安全插件中的管理角色映射面板

Role 下拉列表中选择新角色(仅未映射的角色可供选择)。

添加相应的后端角色。(我使用 LDAP 组名的 CN,因为我已在安全配置中配置了 rolename: 'cn'。如果没有此配置,则需要在此处指定完整的 DN。)

单击 Submit,保存映射:

Open Distro for Elasticsearch 中用于创建新角色映射的对话框

使用 CLI 创建角色和映射

我们将使用命令行界面 (CLI) 创建只读角色和映射。

在新的设置中,您也可使用 plugins/opendistro_security/securityconfig/ 中的默认安全配置文件从命令行创建角色和角色映射。在既有的设置中,从正在运行的集群 (securityadmin.sh -r…) 检索当前安全插件配置,并使用这些输出文件以确保更新当前配置。

$ /usr/share/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh \
    -cacert /etc/elasticsearch/root-ca.pem \
-cert /etc/elasticsearch/kirk.pem \ 
-key /etc/elasticsearch/kirk-key.pem \
-h <Cluster-IP-or-FQDN> \
-r

这将在本地文件夹中创建五个带有日期戳的文件 – config、roles、roles_mapping、internal_users、action_groups

roles 文件复制到 roles.yml 并将 roles_mapping 文件复制到 roles_mapping.yml

将以下所示的配置附加到 roles.yml 文件。这将创建一个名为 CustomReadOnly 的新角色且具有读权限

CustomReadOnly:
  cluster:
  - "CLUSTER_COMPOSITE_OPS_RO"
  indices:
    '*':
      '*':
      - "READ"

将以下所示的配置附加到 roles_mapping.yml。这会将角色 CustomReadOnly 映射到 LDAP 组 ES-Read-Grp

CustomReadOnly:
  backendroles:
  - "ES-Read-Grp" hosts: [] users: []

现在,运行 securityadmin.sh 以更新安全插件配置,且一次推送一个更新,如下所示:

第一个命令行推送角色。

$ /usr/share/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh \
    -cacert /etc/elasticsearch/root-ca.pem \
-cert /etc/elasticsearch/kirk.pem \ 
-key /etc/elasticsearch/kirk-key.pem \
-h <Cluster-IP-or-FQDN> \
-f ./roles.yml -t roles

此命令行推送角色映射。

$ /usr/share/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh \
    -cacert /etc/elasticsearch/root-ca.pem \
-cert /etc/elasticsearch/kirk.pem \ 
-key /etc/elasticsearch/kirk-key.pem \
-h <Cluster-IP-or-FQDN> \
-f ./roles_mapping.yml -t rolesmapping

测试以不同的用户身份登录

以读写用户 esuser1 身份登录

用户 esuser1 属于 ESAdmins LDAP 组并映射到安全角色 ESAdminRole。作为该角色的一部分,允许此用户执行读写操作。

创建索引(正如所料成功):

$ curl -XPUT -k "https://odfe-node1:9200/my-index" -u esuser1
Enter host password for user 'esuser1':
{
    "acknowledged":true,
    "shards_acknowledged":true,
    "index":"my-index"
 }

添加文档(正如所料成功):

$ curl -XPOST -k "https://odfe-node1:9200/my-index/doc1/?pretty=true" -H 'Content-Type: application/json' -d'
        { 
         "text":"Hello World!"
        }' -u esuser1
Enter host password for user 'esuser1':
{
  "_index" : "my-index",
  "_type" : "doc1",
  "_id" : "GPxOLGoB9R98haWaBOPk",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

运行搜索查询(正如所料成功):

$ curl -XPOST -k "https://odfe-node1:9200/_search?pretty=true"  -H 'Content-Type: application/json' -d' 
{ 
 "query":{
    "query_string":{
        "query":"hello"
        }
    }
}' -u esuser1
Enter host password for user 'esuser1':
{
  "took" : 99,
  "timed_out" : false,
  "_shards" : {
    "total" : 90,
    "successful" : 90,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "my-index",
        "_type" : "doc1",
        "_id" : "GPxOLGoB9R98haWaBOPk",
        "_score" : 0.2876821,
        "_source" : {
          "text" : "Hello World!"
        }
      }
    ]
  }
}

以只读用户 esuser2 身份登录

用户 esuser2 属于 ES-Read-Grp LDAP 组并映射到安全角色 CustomReadOnly。作为该角色的一部分,允许此用户执行只读操作。

创建索引(正如所料失败。esuser2 不是允许写入的组的成员):

$ curl -XPUT -k "https://odfe-node1:9200/my-index" -u esuser2
Enter host password for user 'esuser2':
{
  "error" : {
    "root_cause" : [
      {
        "type" : "security_exception",
        "reason" : "no permissions for [indices:data/write/index] and User [name=esuser2, roles=[Domain Users, Users, AWS Delegated Add Workstations To Domain Users], requestedTenant=null]"
      }
    ],
    "type" : "security_exception",
    "reason" : "no permissions for [indices:data/write/index] and User [name=esuser2, roles=[Domain Users, Users, AWS Delegated Add Workstations To Domain Users], requestedTenant=null]"
  },
  "status" : 403
}

运行搜索查询(正如所料成功):

$ curl -XPOST -k "https://odfe-node1:9200/_search?pretty=true"  -H 'Content-Type: application/json' -d' 
{ 
 "query":{
    "query_string":{
        "query":"hello"
        }
    }
}' -u esuser2
Enter host password for user 'esuser2':
{
  "took" : 101,
  "timed_out" : false,
  "_shards" : {
    "total" : 90,
    "successful" : 90,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "my-index",
        "_type" : "doc1",
        "_id" : "GPxOLGoB9R98haWaBOPk",
        "_score" : 0.2876821,
        "_source" : {
          "text" : "Hello World!"
        }
      }
    ]
  }
}

以用户 esuser3(不属于任何组)身份登录

用户 esuser3 不属于任何 LDAP 组,也没有映射到任何安全角色。因此,不允许此用户执行任何操作。

添加文档(正如所料失败。esuser3 不是任何组的成员):

$ curl -XPOST -k "https://odfe-node1:9200/my-index/doc1/?pretty=true" -H 'Content-Type: application/json' -d'
        { 
         "text":"Hello World!"
        }' -u esuser3
Enter host password for user 'esuser3':
  "error" : {
    "root_cause" : [
      {
        "type" : "security_exception",
        "reason" : "no permissions for [indices:data/write/index] and User [name=esuser3, roles=[], requestedTenant=null]"
      }
    ],
    "type" : "security_exception",
    "reason" : "no permissions for [indices:data/write/index] and User [name=esuser3, roles=[], requestedTenant=null]"
  },
  "status" : 403
}

添加搜索查询(正如所料失败。esuser3 不是任何组的成员):

$ curl -XPOST -k "https://odfe-node1:9200/_search?pretty=true"  -H 'Content-Type: application/json' -d' 
{ 
 "query":{
    "query_string":{
        "query":"hello"
        }
    }
}' -u esuser3
Enter host password for user 'esuser3':
{
  "error" : {
    "root_cause" : [
      {
        "type" : "security_exception",
        "reason" : "no permissions for [indices:data/read/search] and User [name=esuser3, roles=[], requestedTenant=null]"
      }
    ],
    "type" : "security_exception",
    "reason" : "no permissions for [indices:data/read/search] and User [name=esuser3, roles=[], requestedTenant=null]"
  },
  "status" : 403
}

小结

在本文中,我介绍了如何将安全插件与 LDAP 或 Active Directory 服务器集成以进行用户身份验证。我展示了如何配置授权以及后端用户组和 Elasticsearch 安全角色之间的映射,以提供基于粒度角色的访问控制。

有关安全插件的 Active Directory 和 LDAP 集成的其他配置选项,请参阅 Open Distro for Elasticsearch 文档。

有问题或疑问? 希望参与讨论? 您可以在我们的论坛上获得帮助并讨论 Open Distro for Elasticsearch。您可以在这里提出问题


本篇作者

Jagadeesh Pusapadi

Jagadeesh Pusapadi 是 AWS 的解决方案架构师,负责与客户就其战略计划进行合作。他通过提供架构指导,帮助客户在 AWS 云上构建创新解决方案,以实现预期的业务成果。