Active Directory 和 LDAP
Active Directory 和 LDAP 既可用于认证(配置的 authc
节),也可用于授权(authz
节)。认证检查用户是否输入了有效的凭证。授权则检索用户的任何后端角色。
在大多数情况下,您会希望同时配置认证和授权。您也可以仅使用认证,并将从 LDAP 检索到的用户直接映射到安全插件角色。
Docker 示例
我们提供了一个功能齐全的示例,可以帮助您了解如何使用 LDAP 服务器进行认证和授权。
- 下载并解压示例 zip 文件。
- 为
admin
用户更新.env
文件,设置一个强密码。 - 在命令行中,运行
docker compose up
。 -
查看文件
-
docker-compose.yml
定义了一个 OpenSearch 单节点、一个 LDAP 服务器以及一个用于 LDAP 服务器的 PHP 管理工具。您可以通过 https://:6443 访问管理工具。确认安全警告并使用
cn=admin,dc=example,dc=org
和changethis
登录。 -
directory.ldif
使用三个用户和两个组来初始化 LDAP 服务器。psantos
属于Administrator
和Developers
组。jroe
和jdoe
属于Developers
组。安全插件将这些组加载为后端角色。 -
roles_mapping.yml
将Administrator
和Developers
LDAP 组(作为后端角色)映射到安全角色,以便用户在认证后获得相应的权限。 -
internal_users.yml
删除所有默认用户,除了administrator
和kibanaserver
。 -
config.yml
包含所有必要的 LDAP 设置。
-
-
以
psantos
身份索引文档curl -XPUT 'https://:9200/new-index/_doc/1' -H 'Content-Type: application/json' -d '{"title": "Spirited Away"}' -u 'psantos:password' -k
如果您以
jroe
身份尝试相同的请求,它将失败。Developers
组映射到readall
、manage_snapshots
和kibana_user
角色,并且没有写入权限。 -
以
jroe
身份搜索文档curl -XGET 'https://:9200/new-index/_search?pretty' -u 'jroe:password' -k
此请求成功,因为
Developers
组映射到readall
角色。 - 如果您想检查各个容器的内容,运行
docker ps
找到容器 ID,然后运行docker exec -it <container-id> /bin/bash
。
连接设置
要启用 LDAP 认证和授权,请将以下行添加到 config/opensearch-security/config.yml
:
内部用户数据库认证也应启用,因为 OpenSearch Dashboards 使用 kibanaserver
内部用户连接到 OpenSearch。
authc:
internal_auth:
order: 0
description: "HTTP basic authentication using the internal user database"
http_enabled: true
transport_enabled: true
http_authenticator:
type: basic
challenge: false
authentication_backend:
type: internal
ldap:
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: basic
challenge: false
authentication_backend:
type: ldap
config:
...
authz:
ldap:
http_enabled: true
transport_enabled: true
authorization_backend:
type: ldap
config:
...
认证和授权的连接设置相同,并添加到 config
节中。
主机名和端口
要配置您的 Active Directory 服务器的主机名和端口,请使用以下设置:
config:
hosts:
- primary.ldap.example.com:389
- secondary.ldap.example.com:389
您可以在此处配置多个服务器。如果安全插件无法连接到第一个服务器,它将尝试按顺序连接到其余服务器。
超时
要配置到您的 Active Directory 服务器的连接和响应超时(值以毫秒为单位),请使用以下设置:
config:
connect_timeout: 5000
response_timeout: 0
如果您的服务器支持双因素认证 (2FA),默认的超时设置可能会导致登录错误。您可以增加 connect_timeout
以适应 2FA 过程。将 response_timeout
设置为 0(默认值)表示无限期等待。
绑定 DN 和密码
要配置安全插件在向您的服务器发出查询时使用的 bind_dn
和 password
,请使用以下设置:
config:
bind_dn: cn=admin,dc=example,dc=com
password: password
如果您的服务器支持匿名认证,bind_dn
和 password
都可以设置为 null
。
TLS 设置
使用以下参数配置连接到服务器的 TLS:
config:
enable_ssl: <true|false>
enable_start_tls: <true|false>
enable_ssl_client_auth: <true|false>
verify_hostnames: <true|false>
名称 | 描述 |
---|---|
enable_ssl | 是否使用基于 SSL 的 LDAP (LDAPS)。 |
enable_start_tls | 是否使用 STARTTLS。不能与 LDAPS 结合使用。 |
enable_ssl_client_auth | 是否将客户端证书发送到 LDAP 服务器。 |
verify_hostnames | 是否验证服务器 TLS 证书的主机名。 |
证书验证
默认情况下,安全插件会根据 opensearch.yml
中配置的根 CA(作为 PEM 证书或信任库)来验证 LDAP 服务器的 TLS 证书。
plugins.security.ssl.transport.pemtrustedcas_filepath: ...
plugins.security.ssl.transport.truststore_filepath: ...
如果您的服务器使用由不同 CA 签名的证书,请将此 CA 导入您的信任库,或将其添加到每个节点的受信任 CA 文件中。
您也可以使用单独的 PEM 格式的根 CA。
为 LDAP 配置单独的根 CA 时,请确保在所有 LDAP config:
设置实例中包含该设置,包括配置的 authc
和 authz
选项。
要配置单独的根 CA,请使用以下配置选项之一:
config:
pemtrustedcas_filepath: /full/path/to/trusted_cas.pem
config:
pemtrustedcas_content: |-
-----BEGIN CERTIFICATE-----
MIID/jCCAuagAwIBAgIBATANBgkqhkiG9w0BAQUFADCBjzETMBEGCgmSJomT8ixk
ARkWA2NvbTEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUxGTAXBgNVBAoMEEV4YW1w
bGUgQ29tIEluYy4xITAfBgNVBAsMGEV4YW1wbGUgQ29tIEluYy4gUm9vdCBDQTEh
...
-----END CERTIFICATE-----
名称 | 描述 |
---|---|
pemtrustedcas_filepath | 包含您的 Active Directory/LDAP 服务器根 CA 的 PEM 文件的绝对路径。 |
pemtrustedcas_content | 您的 Active Directory/LDAP 服务器的根 CA 内容。当设置了 pemtrustedcas_filepath 时,不能使用此项。 |
客户端认证
如果您使用 TLS 客户端认证,安全插件会发送节点在 opensearch.yml
中配置的 PEM 证书。设置以下配置选项之一:
config:
pemkey_filepath: /full/path/to/private.key.pem
pemkey_password: private_key_password
pemcert_filepath: /full/path/to/certificate.pem
或
config:
pemkey_content: |-
-----BEGIN PRIVATE KEY-----
MIID2jCCAsKgAwIBAgIBBTANBgkqhkiG9w0BAQUFADCBlTETMBEGCgmSJomT8ixk
ARkWA2NvbTEXMBUGCgmSJomT8ixkARkWB2V4YW1wbGUxGTAXBgNVBAoMEEV4YW1w
bGUgQ29tIEluYy4xJDAiBgNVBAsMG0V4YW1wbGUgQ29tIEluYy4gU2lnbmluZyBD
...
-----END PRIVATE KEY-----
pemkey_password: private_key_password
pemcert_content: |-
-----BEGIN CERTIFICATE-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHRZwzwGlP2FvL
oEzNeDu2XnOF+ram7rWPT6fxI+JJr3SDz1mSzixTeHq82P5A7RLdMULfQFMfQPfr
WXgB4qfisuDSt+CPocZRfUqqhGlMG2l8LgJMr58tn0AHvauvNTeiGlyXy0ShxHbD
...
-----END CERTIFICATE-----
名称 | 描述 |
---|---|
pemkey_filepath | 包含您的证书私钥的文件的绝对路径。 |
pemkey_content | 您的证书私钥的内容。当设置了 pemkey_filepath 时,不能使用此项。 |
pemkey_password | 私钥的密码(如果有)。 |
pemcert_filepath | 客户端证书的绝对路径。 |
pemcert_content | 客户端证书的内容。当设置了 pemcert_filepath 时不能使用。 |
启用的密码套件和协议
您可以限制 LDAP 连接允许的密码套件和 TLS 协议。例如,您可以只允许强密码套件并将 TLS 版本限制为最新版本:
ldap:
http_enabled: true
transport_enabled: true
...
authentication_backend:
type: ldap
config:
enabled_ssl_ciphers:
- "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"
- "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"
enabled_ssl_protocols:
- "TLSv1.1"
- "TLSv1.2"
名称 | 描述 |
---|---|
enabled_ssl_ciphers | 数组,启用的 TLS 密码套件。仅支持 Java 格式。 |
enabled_ssl_protocols | 数组,启用的 TLS 协议。仅支持 Java 格式。 |
使用 Active Directory 和 LDAP 进行认证
要使用 Active Directory/LDAP 进行认证,首先在 config/opensearch-security/config.yml
的 authc
节中配置相应的认证域:
authc:
ldap:
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: basic
challenge: true
authentication_backend:
type: ldap
config:
...
接下来,将 Active Directory/LDAP 服务器的连接设置添加到认证域的配置节中。
config:
enable_ssl: true
enable_start_tls: false
enable_ssl_client_auth: false
verify_hostnames: true
hosts:
- ldap.example.com:8389
bind_dn: cn=admin,dc=example,dc=com
password: passw0rd
认证的工作方式是,针对 LDAP 树的用户子树发出包含用户名的 LDAP 查询。
安全插件首先获取配置的 LDAP 查询,并将占位符 {0}
替换为用户凭证中的用户名。
usersearch: '(sAMAccountName={0})'
然后,它针对用户子树发出此查询。目前,将搜索配置的 userbase
下的整个子树。
userbase: 'ou=people,dc=example,dc=com'
如果查询成功,安全插件将从 LDAP 条目中检索用户名。您可以指定安全插件应使用 LDAP 条目中的哪个属性作为用户名。
username_attribute: uid
如果此键未设置或为 null,则使用 LDAP 条目的专有名称 (DN)。
配置摘要
名称 | 描述 |
---|---|
userbase | 指定目录中存储用户信息子树的位置。 |
usersearch | 安全插件在尝试认证用户时执行的实际 LDAP 查询。变量 {0} 将被替换为用户名。 |
username_attribute | 安全插件使用目录条目的此属性查找用户名。如果设置为 null,则使用 DN(默认)。 |
完整认证示例
ldap:
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: basic
challenge: true
authentication_backend:
type: ldap
config:
enable_ssl: true
enable_start_tls: false
enable_ssl_client_auth: false
verify_hostnames: true
hosts:
- ldap.example.com:636
bind_dn: cn=admin,dc=example,dc=com
password: password
userbase: 'ou=people,dc=example,dc=com'
usersearch: '(sAMAccountName={0})'
username_attribute: uid
使用 Active Directory 和 LDAP 进行授权
要使用 Active Directory/LDAP 进行授权,首先在 config.yml
的 authz
节中配置相应的授权域:
authz:
ldap:
http_enabled: true
transport_enabled: true
authorization_backend:
type: ldap
config:
...
授权是从 LDAP 服务器为认证用户检索后端角色的过程。这通常与您用于认证的服务器相同,但您也可以使用不同的服务器。唯一的要求是您用于获取角色的用户确实存在于 LDAP 服务器上。
因为安全插件始终检查 LDAP 服务器中是否存在用户,所以您还必须在 authz
节中配置 userbase
、usersearch
和 username_attribute
。
授权的工作方式与认证类似。安全插件针对 LDAP 树的角色子树发出包含用户名的 LDAP 查询。
作为替代,安全插件还可以获取在用户子树中定义为用户条目直接属性的角色。
方法 1:查询角色子树
安全插件首先获取用于获取角色的 LDAP 查询(“rolesearch”),并替换查询中找到的任何变量。例如,对于标准的 Active Directory 安装,您将使用以下角色搜索:
rolesearch: '(member={0})'
您可以使用以下变量:
{0}
被替换为用户的 DN。{1}
被替换为用户名,由username_attribute
设置定义。{2}
被替换为认证用户目录条目中的任意属性值。
变量 {2}
指的是用户目录条目中的一个属性。您应该使用的属性由 userroleattribute
设置指定。
userroleattribute: myattribute
然后,安全插件针对配置的角色子树发出替换后的查询。rolebase
下的整个子树都会被搜索。
rolebase: 'ou=groups,dc=example,dc=com'
如果您使用嵌套角色(其他角色的成员角色),您可以配置安全插件来解析它们。
resolve_nested_roles: false
获取所有角色后,安全插件从角色条目的可配置属性中提取最终的角色名称。
rolename: cn
如果未设置此项,则使用角色条目的 DN。您现在可以使用此角色名称将其映射到一个或多个安全插件角色,如 roles_mapping.yml
中所定义。
方法 2:使用用户属性作为角色名称
如果您将角色存储为用户子树中用户条目的直接属性,则只需配置属性名称。
userrolename: roles
您可以配置多个属性名称。
userrolename: roles, otherroles
此方法可以与查询角色子树结合使用。安全插件从用户的角色属性中获取角色,然后执行角色搜索。
如果您不使用或没有角色子树,可以完全禁用角色搜索。
rolesearch_enabled: false
(高级)控制 LDAP 用户属性
默认情况下,安全插件读取所有 LDAP 用户属性,并将其用于索引名称变量替换和 DLS 查询变量替换。如果您的 LDAP 条目有很多属性,您可能希望控制哪些属性可用。属性越少,性能越好。
请注意,此设置在 config.yml 文件的认证 authc
节中进行。
名称 | 描述 |
---|---|
custom_attr_allowlist | 字符串数组。指定应可用于变量替换的 LDAP 属性。 |
custom_attr_maxval_len | 整数。指定每个属性的最大允许长度。所有超过此长度的属性都将被丢弃。值为 0 会完全禁用自定义属性。默认值为 36。 |
示例
authc:
ldap:
http_enabled: true
transport_enabled: true
authentication_backend:
type: ldap
config:
custom_attr_allowlist:
- attribute1
- attribute2
custom_attr_maxval_len: 36
...
(高级)从角色查找中排除特定用户
如果您使用多种认证方法,从 LDAP 角色查找中排除某些用户可能是有意义的。
考虑一个典型的 OpenSearch Dashboards 设置的以下场景:所有 OpenSearch Dashboards 用户都存储在 LDAP/Active Directory 服务器中。
但是,您还有一个 OpenSearch Dashboards 服务器用户。OpenSearch Dashboards 使用此用户管理存储的对象并执行监控和维护任务。您不希望将此用户添加到您的 Active Directory 安装中,而是将其存储在安全插件的内部用户数据库中。
在这种情况下,将 OpenSearch Dashboards 服务器用户从 LDAP 授权中排除是合理的,因为我们已经知道没有相应的条目。您可以使用 skip_users
配置设置来定义应跳过哪些用户。支持通配符和正则表达式。
skip_users:
- kibanaserver
- 'cn=Jane Doe,ou*people,o=TEST'
- '/\S*/'
(高级)从嵌套角色查找中排除角色
如果您的 LDAP 安装中的用户拥有大量角色,并且您需要解析嵌套角色,则可能会遇到性能问题。
然而,在大多数情况下,并非所有用户角色都与 OpenSearch 和 OpenSearch Dashboards 相关。您可能只需要少数几个角色。在这种情况下,您可以使用嵌套角色过滤功能来定义一个角色列表,这些角色将从用户角色列表中过滤掉。支持通配符和正则表达式。
这仅在 resolve_nested_roles
为 true
时才有效。
nested_role_filter:
- 'cn=Jane Doe,ou*people,o=TEST'
- ...
配置摘要
名称 | 描述 |
---|---|
rolebase | 指定目录中存储角色/组信息的子树位置。 |
rolesearch | 安全插件在尝试确定用户角色时执行的实际 LDAP 查询。您可以在此处使用三个变量(见下文)。 |
userroleattribute | 用户条目中用于 {2} 变量替换的属性。 |
userrolename | 如果用户的角色/组未存储在组子树中,而是作为用户目录条目的属性存储,请在此处定义此属性名称。 |
rolename | 应作为角色名称使用的角色条目的属性。 |
resolve_nested_roles | 布尔值。是否解析嵌套角色。默认值为 false 。 |
max_nested_depth | 整数。当 resolve_nested_roles 为 true 时,此项定义要遍历的最大嵌套角色数。设置较小的值可以减少从 LDAP 检索的数据量并缩短认证时间,但代价是可能无法发现深度嵌套的角色。默认值为 30 。 |
skip_users | 检索角色时应跳过的用户数组。支持通配符和正则表达式。 |
exclude_roles | 检索角色时应排除的角色数组。支持通配符。 |
nested_role_filter | 在解析嵌套角色之前应过滤的角色 DN 数组。支持通配符和正则表达式。 |
rolesearch_enabled | 布尔值。启用或禁用角色搜索。默认值为 true 。 |
custom_attr_allowlist | 字符串数组。指定应可用于变量替换的 LDAP 属性。 |
custom_attr_maxval_len | 整数。指定每个属性的最大允许长度。所有超过此长度的属性都将被丢弃。值为 0 会完全禁用自定义属性。默认值为 36。 |
custom_return_attributes | 字符串数组。指定要从 LDAP 服务器请求的属性。 |
完整授权示例
authz:
ldap:
http_enabled: true
transport_enabled: true
authorization_backend:
type: ldap
config:
enable_ssl: true
enable_start_tls: false
enable_ssl_client_auth: false
verify_hostnames: true
hosts:
- ldap.example.com:636
bind_dn: cn=admin,dc=example,dc=com
password: password
userbase: 'ou=people,dc=example,dc=com'
usersearch: '(uid={0})'
username_attribute: uid
rolebase: 'ou=groups,dc=example,dc=com'
rolesearch: '(member={0})'
userroleattribute: null
userrolename: none
rolename: cn
resolve_nested_roles: true
skip_users:
- kibanaserver
- 'cn=Jane Doe,ou*people,o=TEST'
- '/\S*/'
(高级)配置多个用户和角色基础
在 authc 和/或 authz 部分配置多个用户库时,请使用以下语法
...
bind_dn: cn=admin,dc=example,dc=com
password: password
users:
primary-userbase:
base: 'ou=people,dc=example,dc=com'
search: '(uid={0})'
secondary-userbase:
base: 'cn=users,dc=example,dc=com'
search: '(uid={0})'
username_attribute: uid
...
类似地,在 authz 部分配置多个角色库时,请使用以下设置
...
username_attribute: uid
roles:
primary-rolebase:
base: 'ou=groups,dc=example,dc=com'
search: '(uniqueMember={0})'
secondary-rolebase:
base: 'ou=othergroups,dc=example,dc=com'
search: '(member={0})'
userroleattribute: null
...
包含多个用户和角色库的完整身份验证和授权示例
authc:
...
ldap:
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: basic
challenge: true
authentication_backend:
type: ldap
config:
enable_ssl: true
enable_start_tls: false
enable_ssl_client_auth: false
verify_hostnames: true
hosts:
- ldap.example.com:636
bind_dn: cn=admin,dc=example,dc=com
password: password
users:
primary-userbase:
base: 'ou=people,dc=example,dc=com'
search: '(uid={0})'
secondary-userbase:
base: 'cn=users,dc=example,dc=com'
search: '(uid={0})'
username_attribute: uid
authz:
ldap:
http_enabled: true
transport_enabled: true
authorization_backend:
type: ldap
config:
enable_ssl: true
enable_start_tls: false
enable_ssl_client_auth: false
verify_hostnames: true
hosts:
- ldap.example.com:636
bind_dn: cn=admin,dc=example,dc=com
password: password
users:
primary-userbase:
base: 'ou=people,dc=example,dc=com'
search: '(uid={0})'
secondary-userbase:
base: 'cn=users,dc=example,dc=com'
search: '(uid={0})'
username_attribute: uid
roles:
primary-rolebase:
base: 'ou=groups,dc=example,dc=com'
search: '(uniqueMember={0})'
secondary-rolebase:
base: 'ou=othergroups,dc=example,dc=com'
search: '(member={0})'
userroleattribute: null
userrolename: none
rolename: cn
resolve_nested_roles: true