LDAP 支持
概述
RabbitMQ 可以使用 LDAP 通过外部 LDAP 服务器执行身份验证和授权。此功能由一个内置插件提供,该插件必须启用。
LDAP 插件通常与另一个插件 rabbitmq_auth_backend_cache
结合使用,以提高延迟并显著降低 LDAP 服务器的负载。
身份验证和授权操作被转换为 LDAP 查询,查询使用 RabbitMQ 运维人员配置的模板。
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 服务器列表、身份验证和授权设置等等。
默认配置允许所有用户访问所有 vhost 中的所有对象,但不会使他们成为管理员。通过配置 LDAP 查询可以限制访问。
LDAP 服务器
为了使插件能够连接到 LDAP 服务器,必须至少配置一个服务器主机名或 IP 地址,使用 auth_ldap.servers
键。如果提供多个值,列表值可以是主机名或 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 秒之间。
为 LDAP 连接使用 TLS
从 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(服务器名称指示)
服务器名称指示 (SNI) 可以为到 LDAP 服务器的传出 TLS 连接配置。如果未设置,则默认值将是用于连接的主机名(请参阅上面的 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.com 的 LDAP 词汇表。
术语 | 描述 |
---|---|
绑定 (Bind) | LDAP 中“身份验证请求”的说法。 |
专有名称 (DN) | 专有名称是 LDAP 目录(树)中的唯一键,用于标识对象(如用户或组)。插件将在身份验证阶段(见下文)将客户端提供的用户名转换为专有名称。可以将 DN 视为文件系统中的绝对文件路径。 |
通用名称 (CN) | 树中对象的简短标识符。此标识符在 LDAP 数据库中的对象类(类型)之间会有所不同。例如,一个人的通用名称将是全名。一个组的通用名称将是该组的名称。可以将 CN 视为文件系统中的文件名。 |
属性 (Attribute) | 对象的属性(键值对)。可以将其视为面向对象编程语言中对象的字段。 |
对象类 (Object Class) | 一组预定义的属性。可以将其视为面向对象语言中的类型(类)。 |
条目 (Entry) | LDAP 数据库实体,例如,一个人或一个组。它具有与其关联的对象类以及一个或多个属性,包括通用名称。由于实体位于 LDAP 数据库树中的某个位置,因此它也必须具有唯一标识它的专有名称。条目是 LDAP 插件查询使用的内容(查找、检查成员资格、比较属性等)。LDAP 数据库必须有一些条目(通常是用户、组),才能在实践中用于 RabbitMQ 身份验证和授权。 |
LDAP 操作流程
为了执行 LDAP 查询,插件将打开与列表中第一个可访问的 LDAP 服务器的连接。然后,根据凭据配置,它将执行匿名绑定或“简单绑定”(使用用户名/密码对向 LDAP 服务器验证用户身份)。用于执行绑定的凭据可以从客户端提供的用户名派生,如下节所述。
如果配置了 vhost 访问查询,则接下来将执行该查询,否则将无条件授予 vhost 访问权限。
此时,可以认为连接已成功协商和建立。例如,应该可以在其上打开通道。在连接上执行的所有进一步操作都将执行授权查询之一。例如,声明队列将执行资源访问查询(如下所述)。将消息发布到主题交换还将额外执行主题访问查询。请参阅访问控制指南以了解更多信息。
用户名和专有名称
在简单绑定阶段,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
每个查询定义一个查询,该查询将确定用户是否具有 vhost 的访问权限,他们是否具有资源的访问权限(例如,交换机、队列、绑定)以及他们具有哪些标签。
请注意较长的 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 中的所有对象的权限,但他们不是系统管理员。
所有采用字符串作为参数的查询类型都支持字符串替换,其中可以替换与正在进行的查询相关的变量。每个查询都支持不同的变量。
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 中搜索组,如果前者为 none
,则在 dn_lookup_base
变量中搜索组。如果两个查找基本变量都设置为 none
,则查询将始终返回 false
。搜索范围可以设置为 subtree
或 single_level
。
subtree
搜索查找基本目录下的所有对象single_level
搜索直接包含在查找基本目录内的组
scope 的默认值为 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 Query
{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
组的成员声明和删除交换机,并允许所有用户执行其他所有操作。
Boolean Queries
{'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"}}]}
]}}
此示例为 “user1” 提供对名为 “test1” 的对象的完全访问权限,并为除 “user1” 以外的所有人提供对 “test2” 的访问权限。
Equals Query
{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}}
]
}
这根据用户的 description 字段中是否存在字符串 “can-declare-exchanges” 和 “can-declare-queues” 来授予声明和删除交换机和队列的权限,并授予所有人写入和读取交换机的权限。
Match Query
{match, StringSubQuery, RESubQuery}
接受一个字符串和一个正则表达式,并检查其中一个字符串是否与另一个字符串匹配。请注意,字符串和正则表达式都是子查询(属于下面的 string
和 attribute
类型)。示例
{resource_access_query, {match, {string, "${name}"},
{string, "^${username}-"}}
}
这允许用户配置、读取和写入名称以他们自己的用户名后跟连字符开头的任何对象。
String Sub-query
{string, Pattern}
仅将参数替换为字符串。由于这返回的是字符串而不是布尔值,因此应在 match
或 equals
查询中使用。有关示例,请参见上文。作为简写,您可以使用纯字符串代替 {string, Pattern}
。
Attribute Sub-query
{attribute, DNPattern, AttributeName}
返回从 LDAP 检索的对象的属性值。由于这返回的是字符串而不是布尔值,因此应在 match
或 equals
查询中使用。有关示例,请参见上文。
Example Configuration
将所有内容整合在一起,这是一个示例配置。它同时使用了标准配置和高级配置文件。这使所有用户都能够访问管理插件,但使他们都不是管理员。虚拟主机的访问权限由每个虚拟主机的组成员身份控制。只有 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}}]}
]
}
].
Troubleshooting
使用 LDAP 进行身份验证和/或授权会在系统中引入另一个活动部件。由于 LDAP 服务器是通过网络访问的,因此 网络故障排除 和 TLS 故障排除 指南中涵盖的一些主题适用于 LDAP。
为了排除身份验证和授权阶段执行的 LDAP 操作故障,强烈建议启用 LDAP 流量日志记录。
ldapsearch 是 LDAP 附带的命令行工具,可以针对 OpenLDAP 安装执行任意 LDAP 查询。这在排除复杂的授权查询故障时非常有用。ldp.exe 是 Active Directory 的对应工具。
LDAP Proxies
LDAP 代理可用于修改此插件执行的 LDAP 请求。ldaptor 是一个库,可用于将自定义逻辑构建到代理中。
操作员应认识到,使用代理将使 LDAP 请求的故障排除更加困难。