LDAP 支持
概述
RabbitMQ 可以使用 LDAP 通过委托给外部 LDAP 服务器来执行 身份验证和授权。此功能由内置插件提供,必须启用。
身份验证和授权操作将被转换为 LDAP 查询,这些查询使用由 RabbitMQ 操作员配置的模板。
为了提高效率并减少 LDAP 服务器的负载,LDAP 查询可以 缓存 一段时间。
LDAP 操作流程 部分提供了关于插件工作原理的更详细概述。
该插件主要针对 OpenLDAP 和 Microsoft Active Directory。其他 LDAP v3 实现应该也能很好地工作。
本指南简要概述了 LDAP 术语,但一般假定您对 LDAP 有基本了解。网上有许多面向初学者的 LDAP 入门指南,例如,一个,两个,以及 LDAP 词汇表。
本指南涵盖了 RabbitMQ 使用的 LDAP 操作流程,LDAP 模型如何 映射到 RabbitMQ 权限模型,如何 使用 TLS 连接到 LDAP 服务器,以及哪些工具可用于 排查故障 和 代理 LDAP 请求。
先决条件
RabbitMQ LDAP 插件依赖于一个名为 eldap
的 LDAP 客户端。该库与 Erlang/OTP 一起提供。在某些操作系统上,Erlang 作为一组软件包而不是一个单一的软件包提供,因此,eldap
等组件**必须单独安装**在主运行时之外。
在 Debian 和 Ubuntu 上,eldap
由 erlang-eldap
软件包提供。
sudo apt-get install -y erlang-eldap
在 Erlang 安装中,如果库不可用,则 **无法** 使用 LDAP 支持。
请参阅 Erlang 兼容性指南 以了解更多信息。
启用插件
LDAP 插件与 RabbitMQ 一起提供。要启用它,请使用 rabbitmq-plugins
rabbitmq-plugins enable rabbitmq_auth_backend_ldap
启用 LDAP AuthN 和 AuthZ 后端
启用插件后,需要配置节点以使用它。
这包括
- 将 LDAP 列为 身份验证 (authN) 和/或授权 (authZ) 后端
- 配置 LDAP 服务器端点
- 指定将使用哪些 LDAP 查询 来执行各种 authZ 权限检查
以下示例将配置 RabbitMQ 以 **仅** 使用 LDAP 进行身份验证和授权,并忽略内部数据库
# use LDAP exclusively for authentication and authorisation
auth_backends.1 = ldap
在 advanced.config
文件 中,相同的设置将如下所示
{rabbit, [
{auth_backends, [rabbit_auth_backend_ldap]}
]}
以下示例将指示节点首先尝试 LDAP,然后在无法通过 LDAP 身份验证用户的情况下回退到内部数据库
# try LDAP first
auth_backends.1 = ldap
# fall back to the internal database
auth_backends.2 = internal
在 advanced.config
格式 中,相同的示例
{rabbit,[
{auth_backends, [rabbit_auth_backend_ldap, rabbit_auth_backend_internal]}
]}
在以下示例中,LDAP 将首先用于身份验证。如果在 LDAP 中找到了用户,则将根据 LDAP 检查密码,并根据内部数据库执行后续的授权检查(因此,LDAP 中的用户也必须存在于内部数据库中,可以选择使用空密码)。如果在 LDAP 中找不到用户,则使用仅内部数据库进行第二次尝试。
# use LDAP for authentication first
auth_backends.1.authn = ldap
# use internal database for authorisation
auth_backends.1.authz = internal
# fall back to the internal database
auth_backends.2 = internal
在 高级配置格式 中
{rabbit,[{auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal},
rabbit_auth_backend_internal]}]}
配置
启用插件及其后端后,必须配置许多 LDAP 特定的设置。这些设置包括 LDAP 服务器列表、身份验证和授权设置等等。
默认配置允许所有用户访问所有虚拟主机中的所有对象,但不将它们设为管理员。可以通过配置 LDAP 查询来限制访问。
LDAP 服务器
为了使插件能够连接到 LDAP 服务器,必须使用 auth_ldap.servers
键配置至少一个服务器主机名或 IP 地址。如果提供多个值,列表值可以是主机名或 IP 地址。必须配置此值。以下示例配置插件以使用两个 LDAP 服务器。它们将按顺序尝试,直到连接到其中一个成功
auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100
使用经典配置格式的相同示例
[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap.eng.megacorp.local", "192.168.0.100"]}
]}
].
LDAP 服务器通常使用端口 389
,这也是 LDAP 插件默认使用的端口。auth_ldap.port
可用于覆盖此设置
auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100
auth_ldap.port = 6389
使用经典配置格式的相同示例
[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap.eng.megacorp.local", "192.168.0.100"]},
{port, 6389}
]}
].
可以使用 auth_ldap.timeout
配置键为与 LDAP 服务器的 TCP 连接设置超时
auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100
# 15 seconds in milliseconds
auth_ldap.timeout = 15000
默认值为 infinity
,即没有超时。
LDAP 服务器连接被池化,以避免过多的连接波动和 LDAP 服务器负载。默认情况下,池最多具有 64 个连接。这可以使用 auth_ldap.connection_pool_size
设置进行控制
auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100
auth_ldap.connection_pool_size = 256
在经过可通过 auth_ldap.idle_timeout
(以毫秒或 infinity
为单位)配置的一段时间后,没有活动的池化连接将被关闭
auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100
auth_ldap.connection_pool_size = 256
# 300 seconds in milliseconds
auth_ldap.idle_timeout = 300000
建议使用 120 到 300 秒之间的值。
使用 TLS 连接到 LDAP 服务器
从 Erlang 26 开始,TLS 客户端对等验证 默认情况下由 TLS 实现启用。
如果未配置客户端 TLS 证书和密钥对,则启用 TLS 的 LDAP 服务器连接将失败。如果不需要对等验证,则可以禁用它,否则必须为 LDAP 连接配置证书和私钥对。
可以使用 TLS 连接到 LDAP 服务器。要指示插件这样做,请将 auth_ldap.use_ssl
设置设置为 true
。如果 LDAP 服务器使用 StartTLS,请改用 auth_ldap.use_starttls
。请注意,这些设置是互斥的(不能组合使用)。这两个值的默认值为 false
。
客户端 TLS 设置使用 ssl_options
配置,这些设置与 RabbitMQ 中其他地方的 TLS 设置 非常相似。
以下是一个最小示例
auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100
# enables TLS for connections to the LDAP server
auth_ldap.use_ssl = true
# Disables peer certificate chain verification. See the section on Peer Verification
# below.
#
# Doing so loses one of the key benefits of TLS and make the setup less secure
# but also simplifies node configuration.
auth_ldap.ssl_options.verify = verify_none
该插件还可以使用 StartTLS 连接。此较旧且不安全的选项 **不建议** 使用,但对于较旧的 LDAP 服务器可能是必需的
auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100
# Enables StartTLS for connections to the LDAP server.
# Prefer auth_ldap.use_ssl with reasonably modern LDAP servers!
auth_ldap.use_starttls = true
# Disables peer certificate chain verification. See the section on Peer Verification
# below.
#
# Doing so loses one of the key benefits of TLS and make the setup less secure
# but also simplifies node configuration.
auth_ldap.ssl_options.verify = verify_none
可用于 LDAP 的客户端 TLS 选项
有许多可用的 TLS 客户端选项
CA 证书、客户端证书和私钥
# local filesystem path to a CA certificate bundle file
auth_ldap.ssl_options.cacertfile = /path/to/ca_certificate.pem
# local filesystem path to a client certificate file
auth_ldap.ssl_options.certfile = /path/to/client_certfile.pem
# local filesystem path to a client private key file
auth_ldap.ssl_options.keyfile = /path/to/client_key.pem
SNI(服务器名称指示)
可以为传出的 TLS 连接到 LDAP 服务器配置 服务器名称指示 (SNI)。如果未设置,则默认值为用于连接的主机名(请参阅上面的 auth_ldap.servers.*
)。
# Sets Server Name Indication for LDAP connections.
# If an LDAP server host is available via multiple domain names, set this value
# to the preferred domain name target LDAP server
auth_ldap.ssl_options.sni = ldap.identity.eng.megacorp.local
主机名验证
主机名验证不应与 对等证书链验证 混淆。这些设置是正交的,可以组合使用。
# take wildcards into account when performing hostname verification
auth_ldap.ssl_options.hostname_verification = wildcard
# disables hostname verification
auth_ldap.ssl_options.hostname_verification = none
对等验证
从 Erlang 26 开始,LDAP TLS 客户端默认情况下会执行 对等证书链验证。
对等证书链验证 不应与主机名匹配验证混淆。这些设置是正交的,可以组合使用。
# Enables peer certificate chain verification.
# This behavior is the default starting with Erlang 26 (and thus RabbitMQ 3.13+)/
auth_ldap.ssl_options.verify = verify_peer
# Disables peer certificate chain verification.
#
# Doing so loses one of the key benefits of TLS and make the setup less secure
# but also simplifies node configuration.
auth_ldap.ssl_options.verify = verify_none
# if target LDAP server does not present a certificate, should the connection be aborted?
auth_ldap.ssl_options.fail_if_no_peer_cert = true
对等链验证深度
可以为使用多个中间证书的服务器增加 证书链验证深度
auth_ldap.ssl_options.depth = 5
启用的 TLS 版本
# use TLSv1.2 only
ssl_options.versions.1 = tlsv1.2
advanced.config
中的 TLS 选项
以下示例使用 advanced.config
格式
[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap1.eng.megacorp.local", "ldap2.eng.megacorp.local"]},
{use_ssl, true},
{ssl_options, [{cacertfile, "/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{verify, verify_peer},
{fail_if_no_peer_cert, true}]},
{server_name_indication, "ldap.identity.eng.megacorp.local"},
{ssl_hostname_verification, wildcard}
]}
].
LDAP 查询缓存以提高效率并减少负载
一个特殊的 缓存后端 可以与其他后端结合使用 结合,以显著减少它们在 LDAP 服务器上产生的负载。
建议生产集群在依赖 LDAP 进行身份验证和授权时,将它与缓存后端结合使用。对于大多数系统来说,15 到 60 秒的缓存间隔在安全性和效率之间取得了良好的平衡。
LDAP 基础知识和术语
本节介绍了本文档中使用的一些基本 LDAP 术语。有关 LDAP 入门的介绍,请参阅 这个概述 Digital Ocean 和 LDAP 词汇表 来自 ldap.com。
术语 | 描述 |
---|---|
绑定 | LDAP 中的“身份验证请求”。 |
可分辨名称 (DN) | 可分辨名称是 LDAP 目录(树)中用于标识对象(如用户或组)的唯一键。插件将在身份验证阶段(见下文)将客户端提供的用户名转换为可分辨名称。您可以将 DN 视为文件系统中的绝对文件路径。 |
通用名称 (CN) | 树中对象的简短标识符。此标识符在 LDAP 数据库中的对象类(类型)之间会不同。例如,一个人的通用名称将是全名。一个组的通用名称将是该组的名称。您可以将 CN 视为文件系统中的文件名。 |
属性 | 对象的属性(键值对)。可以把它看作面向对象编程语言中对象的字段。 |
对象类 | 一组预定义的属性。可以把它看作面向对象语言中的类型(类)。 |
条目 | LDAP 数据库实体,例如,人或组。它与一个对象类相关联,并且具有一个或多个属性,包括一个通用名称。由于实体位于 LDAP 数据库树中的某个位置,因此它还必须具有唯一标识它的可分辨名称。条目是 LDAP 插件查询(查找、检查成员资格、比较属性等)使用的对象。LDAP 数据库必须具有一些条目(通常是用户、组)才能在实践中用于 RabbitMQ 身份验证和授权。 |
LDAP 操作流程
为了执行 LDAP 查询,插件将打开到列表中第一个可到达的 LDAP 服务器的连接。然后,根据凭据配置,它将执行匿名绑定或“简单绑定”(使用用户名/密码对对用户进行 LDAP 服务器身份验证)。用于执行绑定的凭据可以从客户端提供的用户名派生,如下一节所述。
如果配置了虚拟主机访问查询,则将首先执行该查询,否则将无条件地授予虚拟主机访问权限。
此时,连接可以被认为已成功协商和建立。例如,应该可以在其上打开一个通道。对连接执行的所有后续操作都将执行授权查询之一。例如,声明队列将执行资源访问查询(在下面介绍)。将消息发布到主题交换机将另外执行主题访问查询。请参阅 访问控制指南 以了解更多信息。
用户名和可分辨名称
在简单绑定阶段,user_dn_pattern
模式用于将提供的用户名转换为用于绑定的值。默认情况下,该模式按原样传递提供的值(即,该模式为 ${username}
)。如果指定了 user_bind_pattern
,则它优先于 user_dn_pattern
。如果在可分辨名称查找阶段需要使用不同的 user_dn_pattern
,这将非常有用。请注意,以上内容不适用于匿名绑定,也不适用于 dn_lookup_bind
未设置为 as_user
的情况。
客户端连接提供用户名,这些用户名被转换为 LDAP 中的可分辨名称 (DN)。有两种方法可以做到这一点。最简单的方法是使用 user_dn_pattern
进行字符串替换。要使用此选项,请将 user_dn_pattern
设置为包含 ${username}
确切一次实例的字符串,这是一个将被替换为客户端提供的用户名值的变量。
例如,将 user_dn_pattern 设置为 "cn=${username},ou=People,dc=example,dc=com"
将导致用户名 simon
被转换为 DN cn=simon,ou=People,dc=example,dc=com
。默认值为 "${username}"
,换句话说,用户名被原样使用。
将用户名转换为可分辨名称的另一种方法是通过 LDAP 查找。为此,请将 auth_ldap.dn_lookup_attribute
设置为代表用户名的属性名称,并将 auth_ldap.dn_lookup_base
设置为查询的基准 DN。可以在两个时间点之一执行查找,即在尝试以所述用户身份绑定之前或之后。
要在绑定后执行查找,请将 auth_ldap.dn_lookup_bind
保持其默认值 as_user
。然后,LDAP 插件将使用用户的普通(未修改)用户名进行绑定以进行登录,然后查找其 DN。为了使此方法起作用,需要将 LDAP 服务器配置为允许使用普通用户名进行绑定(Microsoft Active Directory 通常会执行此操作)。
要在绑定之前执行查找,请按如下方式设置 dn_lookup_bind
、dn_lookup_base
和 dn_lookup_attribute
。然后,LDAP 插件将首先使用这些凭据进行绑定以进行查找,然后使用用户的 DN 和密码进行绑定以进行登录。
auth_ldap.dn_lookup_bind.user_dn = CN=myuser,OU=users,DC=gopivotal,DC=com
auth_ldap.dn_lookup_bind.password = test1234
auth_ldap.dn_lookup_attribute = userPrincipalName
auth_ldap.dn_lookup_base = DC=gopivotal,DC=com
考虑以下示例
auth_ldap.dn_lookup_attribute = userPrincipalName
auth_ldap.dn_lookup_base = DC=gopivotal,DC=com
使用此配置,可以使用电子邮件地址进行身份验证(userPrincipalName
值通常是电子邮件地址),并让本地 Active Directory 服务器返回实际的 DN 以进行登录。
如果同时设置了 auth_ldap.dn_lookup_attribute
和 auth_ldap.user_dn_pattern
,则将结合这两种方法:插件将填写模板,然后搜索 DN。
auth_ldap.dn_lookup_bind
的默认值为 as_user
。对于 auth_ldap.dn_lookup_base
和 auth_ldap.dn_lookup_attribute
,它是 none
。
LDAP 活动日志记录
插件可以使用 auth_ldap.log
(在经典配置格式中为 rabbitmq_auth_backend_ldap.log
)设置来控制 LDAP 活动日志记录的详细程度。这对于故障排除至关重要。
将该值设置为 true
将启用 LDAP 插件用于做出决策的逻辑的详细日志记录。在这种模式下,绑定请求结果中的凭据将被清除。此模式不推荐用于生产系统,但有时可能会有用。
network
的值与上述类似,但会另外导致在较低(LDAP 客户端)级别记录 LDAP 网络流量,并将绑定请求凭据清除。此设置可能会导致过度日志记录,应谨慎使用。
network_unsafe
的值会导致在较低(LDAP 客户端)级别记录 LDAP 网络流量,并将绑定请求凭据(如密码)写入日志;此模式不得在生产环境中使用,因为它会违反几乎所有广泛采用的安全策略。但是,它对于在开发和 QA 环境中进行故障排除非常有用。
最后,false
(默认值)将停用 LDAP 流量日志记录。
以下示例将 LDAP 日志记录级别设置为 network
auth_ldap.log = network
经典配置格式中的相同示例
[
{rabbitmq_auth_backend_ldap, [
%% ...
{log, network}
]}
]
用于授权查询的绑定
对于身份验证,此插件会以它试图验证的用户身份绑定到 LDAP 服务器。other_bind
设置控制如何为授权查询绑定,以及如何检索未提供密码登录的用户的详细信息(例如,使用 EXTERNAL 身份验证机制)。
可接受的值为 as_user
(以已验证的用户身份绑定)或 anon
(匿名绑定),或者由两个选项 other_bind.user_dn
和 other_bind.password
表示,以使用指定的用户名和密码进行绑定。例如
auth_ldap.other_bind.user_dn = a-username
auth_ldap.other_bind.password = a-password
使用经典配置格式
[
{rabbitmq_auth_backend_ldap, [
{other_bind, {"a-username", "a-password"}}
]}
].
请注意,在用户连接时未提供密码的情况下,无法使用默认的 as_user
配置。在这种情况下,请使用 auth_ldap.other_bind.user_dn
和 auth_ldap.other_bind.password
或 anon
选项。
auth_ldap.other_bind
的默认值为 as_user
。
组成员资格查找
插件支持多个组成员资格查找查询。group_lookup_base
设置控制用于搜索嵌套组的基准 DN。它仅由 {in_group_nested, ...}
查询使用。有关更多信息,请参阅 有关查询的部分。
在以下示例中,ou=groups,dc=example,dc=com
是包含所有组的目录。请注意,它使用的是 经典配置格式
[
{rabbitmq_auth_backend_ldap, [
%% ...
{group_lookup_base, "ou=groups,dc=example,dc=com"}
]}
]
默认值为 'none'
。
配置授权
RabbitMQ 权限模型如何映射到 LDAP
RabbitMQ 权限模型 与 LDAP 的权限模型不同。此外,使用 LDAP 架构的方式因公司而异。因此,需要一种机制来定义 RabbitMQ 授权功能使用哪些 LDAP 请求。授权由四个可配置的查询控制
rabbitmq_auth_backend_ldap.vhost_access_query
rabbitmq_auth_backend_ldap.resource_access_query
rabbitmq_auth_backend_ldap.topic_access_query
rabbitmq_auth_backend_ldap.tag_queries
每个查询都定义了一个查询,用于确定用户是否可以访问虚拟主机,是否可以访问资源(例如,交换机、队列、绑定)以及他们拥有哪些 标签。
请注意较长的 rabbitmq_auth_backend_ldap
前缀。查询使用用 Erlang 术语(数据结构)表示的特定于域的语言来表示,因此只能使用 高级配置格式 进行定义。
查询及其类型
上面提到的每个查询都在不同的授权阶段使用,并且必须计算为 true
或 false。特定的查询类型(表达式,例如,值比较或组成员资格检查)将在本指南的后面部分介绍。
查询可以是多种类型之一。每种类型代表一个布尔表达式或函数:比较、字符串匹配、对象存在性检查、组成员资格检查等。查询可以嵌套,并且可以使用布尔运算符进行组合。
默认值(表达式)可以在下表中找到
查询 | 目的 | 默认表达式 |
rabbitmq_auth_backend_ldap.vhost_access_query | 验证用户是否被允许访问虚拟主机 | {constant, true} |
rabbitmq_auth_backend_ldap.resource_access_query | 验证用户是否被允许访问资源(队列、交换机等) | {constant, true} |
rabbitmq_auth_backend_ldap.topic_access_query | 验证用户是否被允许发布到主题 | {constant, true} |
rabbitmq_auth_backend_ldap.tag_queries | 检查用户是否适用已知标签 | [{administrator, {constant, false}}] |
这意味着所有用户都被授予对所有虚拟主机中所有对象的访问权限,但他们不是系统管理员。
所有将字符串作为参数的查询类型都支持字符串替换,其中可以替换与正在进行的查询相关的变量。每个查询都支持不同的变量。
vhost_access_query
支持
${username}
:身份验证时提供的用户名${user_dn}
:用户的识别名称${vhost}
:我们正在查询访问权限的虚拟主机
resource_access_query
支持
${username}
:身份验证时提供的用户名${user_dn}
:用户的识别名称${vhost}
:资源所在的虚拟主机${resource}
:资源类型,可以是 "exchange" 或 "queue"${name}
:资源的名称${permission}
:对资源请求的访问类型,可以是 "configure"、"write" 或 "read"
tag_queries
支持
${username}
:身份验证时提供的用户名${user_dn}
:用户的识别名称${vhost}
:**并非所有情况下都可用虚拟主机信息**。它可以用于提供额外的上下文,例如对应用程序或用户进行分组
topic_access_query
支持
${username}
:身份验证时提供的用户名${user_dn}
:用户的识别名称${vhost}
:资源所在的虚拟主机${resource}
:在这种情况下始终为 "topic"${name}
:资源的名称${permission}
:可以是 "write"(发布)或 "read"(消费、队列和交换机到交换机的绑定,用于主题交换机)${routing_key}
:已发布消息的路由键("write" 权限)或主题交换机到队列/交换机绑定的路由键("read" 权限)
最后,如果身份验证时提供的用户名采用 Domain\User
格式(在某些 Active Directory 环境中就是这样),则上述每个查询都会提供两个 *额外* 的变量
${ad_domain}
-Domain\User
中的域部分${ad_user}
-Domain\User
中的用户部分
资源访问的术语 "configure"、"write" 和 "read" 与内置 RabbitMQ 权限系统中的含义相同,请参见 ./access-control。有关 topic_access_query
,另请参见 主题授权。
第一次熟悉查询 DSL 时,打开上面记录的 log
配置参数会有所帮助。这会导致 LDAP 插件向 RabbitMQ 日志写入对它执行的查询及其因此做出的决定的相当详细的描述。
虚拟主机访问
rabbitmq_auth_backend_ldap.vhost_access_query
是用于控制虚拟主机访问权限的查询。如果查询结果为 true
,则授予访问权限。
请注意,在用户能够访问虚拟主机之前,必须在 RabbitMQ 中创建该虚拟主机;与用户和权限不同,虚拟主机不能完全存在于 LDAP 中。
用户标签
tag_queries
由一个键值映射组成,它将标签的名称映射到要执行的查询,以确定用户是否拥有该标签。需要列出用户应该拥有所有标签的查询列表。
授权查询参考
常量查询
{constant, Bool}
这将始终返回 true 或 false,无条件地授予或拒绝访问权限。示例
{tag_queries, [{administrator, {constant, false}},
{management, {constant, true}}]}
这授予所有用户使用管理插件的能力,但不会使他们成为管理员。
存在查询
{exists, Pattern}
这将把变量替换到模式中,如果存在具有结果 DN 的对象,则返回 true。示例
{vhost_access_query, {exists, "ou=${vhost},ou=vhosts,dc=example,dc=com"}}
这授予对所有虚拟主机的访问权限,这些虚拟主机存在于 ou=vhosts,dc=example,dc=com
中的组织单位,所有用户都拥有访问权限。
在组查询
{in_group, Pattern}
{in_group, Pattern, AttributeName}
与存在查询类似,将参数替换到模式中以查找对象。但是,此查询返回 true,如果已登录用户是成员;针对 member
属性或任何命名属性进行检查。示例
{vhost_access_query, {in_group, "cn=${vhost}-users,ou=vhosts,dc=example,dc=com"}}
当用户在 ou=vhosts,dc=example,dc=com
中的适当命名对象(例如 groupOfNames
)中列为 member
属性时,这会授予对虚拟主机的访问权限。
在嵌套组查询
{in_group_nested, Pattern}
{in_group_nested, Pattern, AttributeName}
{in_group_nested, Pattern, AttributeName, Scope}
类似于 in_group
查询,但也会遍历组层次结构,例如,如果已登录用户是某个组的成员,而该组又是另一个组的成员。成员资格针对 member
属性或任何命名属性进行检查。组在由 group_lookup_base
配置键定义的 DN 或 dn_lookup_base
变量(如果前者为 none
)中搜索。如果两个查找基变量都设置为 none
,则查询将始终返回 false
。搜索范围可以设置为 subtree
或 single_level
。
subtree
搜索查找基下的所有对象single_level
搜索直接包含在查找基中的组
范围的默认值为 subtree
。查询使用从用户到目标组的深入搜索。搜索过程将检测并跳过循环路径。如果用户是许多组的成员,而这些组又是许多组的成员,则此查询可能会占用大量时间和内存。当组用于成员资格层次结构时,使用此查询。如果可能,仍然建议使用普通的 {in_group, ...}
查询:嵌套组可能难以推断。示例
[
{group_lookup_base, "ou=groups,dc=example,dc=com"},
{vhost_access_query, {in_group_nested, "cn=${vhost}-groups,ou=groups,dc=example,dc=com"}, "member", single_level}
]
当用户是 member
属性值定义的组层次结构的成员,并且位于 ou=groups,dc=example,dc=com
目录中时,这会授予对虚拟主机的访问权限。
对于查询
{for, [{Name, Value, SubQuery}, ...]}
这允许您拆分查询并使用不同的子查询处理不同的情况。
选项应该是一个三元组列表,每个三元组包含一个名称、一个值和一个子查询。名称是变量的名称(即在 ${}
替换中使用的内容)。值是该变量的可能值。
请注意,值是不同的 Erlang 类型;resource
和 permission
具有原子值(例如 resource
可以是 exchange
),而其他键具有二进制值(例如 name
可能为 <<"amq.fanout">>
)。
示例
{resource_access_query,
{for, [{resource, exchange, {for, [{permission, configure,
{in_group, "cn=wheel,dc=example,dc=com"}
},
{permission, write, {constant, true}},
{permission, read, {constant, true}}
]}},
{resource, queue, {constant, true}}]}}
这允许 wheel
组的成员声明和删除交换机,并允许所有用户执行所有其他操作。
布尔查询
{'not', SubQuery}
{'and', [SubQuery1, SubQuery2, SubQuery3, ...]}
{'or', [SubQuery1, SubQuery2, SubQuery3, ...]}
这些可用于将子查询与布尔逻辑结合起来。'and' 和 'or' 查询都接受任意长的子查询列表,如果所有或任何子查询分别计算为 true,则返回 true。
请注意,'and'、'or' 和 'not' 是 Erlang 中的保留字,因此在配置文件中,关键字需要用单引号引起来,如上所示。示例
{'or',
[{'and',
[{equals, "${name}", "test1"},
{equals, "${username}", "user1"}]},
{'and',
[{equals, "${name}", "test2"},
{'not', {equals, "${username}", "user1"}}]}
]}}
此示例授予对名为 "test1" 到 "user1" 的对象的完全访问权限,并授予对 "test2" 的访问权限,但 "user1" 除外。
相等查询
{equals, StringSubQuery1, StringSubQuery2}
接受两个字符串,并检查其中一个是否与另一个匹配。请注意,两个字符串都是依次进行的子查询(如以下的 string
和 attribute
类型)。
这对于比较其中一个字符串替换变量的值与常量、属性值等很有用。示例
{resource_access_query,
{for, [{permission, configure, {equals, {attribute, "${user_dn}", "description"},
{string, "can-declare-${resource}s"}
}
},
{permission, write, {constant, true}},
{permission, read, {constant, true}}
]
}
这根据用户描述字段中是否存在字符串 "can-declare-exchanges" 和 "can-declare-queues" 来授予声明和删除交换机和队列的权限,并授予所有人写入和读取交换机的权限。
匹配查询
{match, StringSubQuery, RESubQuery}
接受一个字符串和一个正则表达式,并检查其中一个是否与另一个匹配。请注意,字符串和正则表达式都是依次进行的子查询(如以下的 string
和 attribute
类型)。示例
{resource_access_query, {match, {string, "${name}"},
{string, "^${username}-"}}
}
这允许用户配置、读取和写入任何名称以其自己的用户名后跟连字符开头的对象。
字符串子查询
{string, Pattern}
只需将参数替换到字符串中。由于这返回的是字符串而不是布尔值,因此应该在 match
或 equals
查询中使用它。请参见上面的示例。作为简写,您可以使用普通字符串而不是 {string, Pattern}
。
属性子查询
{attribute, DNPattern, AttributeName}
返回从 LDAP 中检索到的对象的属性值。由于这返回的是字符串而不是布尔值,因此应该在 match
或 equals
查询中使用它。请参见上面的示例。
示例配置
将所有内容整合在一起,这里有一个示例配置。它同时使用了 标准配置 和 高级配置 文件。这使得所有用户能够访问管理插件,但不会使他们成为管理员。对虚拟主机的访问权限由每个虚拟主机的组成员资格控制。只有 admin
的成员才能声明、删除或绑定交换机和队列,但所有用户都可以发布到交换机并从队列中声明。发布到主题类型交换机仅限于路由键以 "a" 开头的消息,并且从主题中消费不受限制(主题授权)。
标准配置 (rabbitmq.conf) 用于配置身份验证后端和几个 LDAP 插件参数
auth_backends.1 = ldap
auth_ldap.servers.1 = my-ldap-server
auth_ldap.user_dn_pattern = cn=${username},ou=People,dc=example,dc=com
auth_ldap.use_ssl = false
auth_ldap.port = 389
auth_ldap.log = false
高级配置 用于定义 LDAP 查询
[{rabbitmq_auth_backend_ldap,[
{vhost_access_query, {in_group,
"ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}},
{resource_access_query,
{for, [{permission, configure, {in_group, "cn=admin,dc=example,dc=com"}},
{permission, write,
{for, [{resource, queue, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, exchange, {constant, true}}]}},
{permission, read,
{for, [{resource, exchange, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, queue, {constant, true}}]}}
]
}},
{topic_access_query,
{for, [{permission, write, {match, {string, "${routing_key}"}, {string, "^a"}}},
{permission, read, {constant, true}}
]
}},
{tag_queries, [{administrator, {constant, false}},
{management, {constant, true}}]}
]}].
或者,您可以使用 经典配置格式 在单个文件中配置所有内容
[
{rabbit, [{auth_backends, [rabbit_auth_backend_ldap]}]},
{rabbitmq_auth_backend_ldap,
[ {servers, ["my-ldap-server"]},
{user_dn_pattern, "cn=${username},ou=People,dc=example,dc=com"},
{use_ssl, false},
{port, 389},
{log, false},
{vhost_access_query, {in_group,
"ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}},
{resource_access_query,
{for, [{permission, configure, {in_group, "cn=admin,dc=example,dc=com"}},
{permission, write,
{for, [{resource, queue, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, exchange, {constant, true}}]}},
{permission, read,
{for, [{resource, exchange, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, queue, {constant, true}}]}}
]
}},
{topic_access_query,
{for, [{permission, write, {match, {string, "${routing_key}"}, {string, "^a"}}},
{permission, read, {constant, true}}
]
}},
{tag_queries, [{administrator, {constant, false}},
{management, {constant, true}}]}
]
}
].
故障排除
使用 LDAP 进行身份验证和/或授权会在系统中引入另一个活动部分。由于 LDAP 服务器是通过网络访问的,因此 网络故障排除 和 TLS 故障排除 指南中涵盖的一些主题适用于 LDAP。
为了对身份验证和授权阶段执行的 LDAP 操作进行故障排除,强烈建议 启用 LDAP 流量日志记录。
ldapsearch 是一个与 LDAP 配套的命令行工具,它使您能够对 OpenLDAP 安装执行任意 LDAP 查询。这在对复杂的授权查询进行故障排除时非常有用。 ldp.exe 是 Active Directory 的对应工具。
LDAP 代理
可以使用 LDAP 代理修改此插件执行的 LDAP 请求。 ldaptor 是一个库,可用于将自定义逻辑构建到代理中。
操作员应注意,使用代理将使 LDAP 请求故障排除 更加困难。