身份验证、授权、访问控制
概述
本文档介绍了 RabbitMQ 中的 身份验证 和 授权 功能。它们共同使操作员能够控制对系统的访问。
不同的用户可以被授予仅访问特定 虚拟主机 的权限。他们在每个虚拟主机中的权限也可以受到限制。
RabbitMQ 支持两种主要的 身份验证机制 以及几种 身份验证和授权后端。
本指南涵盖了各种身份验证、授权和用户管理主题,例如
- 访问控制要点
- 默认虚拟主机和用户
- 对默认用户施加的 连接限制
- 授权和资源权限
- 如何使用 CLI 工具 管理用户和权限
- 如何更改 使用的身份验证或授权后端,或使用后端组合
- 如何 使用 TLS 证书信息对客户端进行身份验证
- 如何限制 对 topic 交换器上 topic 的访问
- 用户标签 及其用法
- 如何轮换凭据以及 撤销 用户的访问权限
- 生成的密码中的 shell 转义 字符
- 如何 预先创建用户 及其权限
- 对 身份验证 和 授权失败 的故障排除
基于 密码 的身份验证有一个配套指南。两个密切相关的主题是 OAuth 2 支持 和 TLS 支持,包括基于 x.509 证书的身份验证,这些都在专门的指南中介绍。
术语和定义
身份验证和授权经常被混淆或互换使用。这是错误的,在 RabbitMQ 中,两者是分开的。为了简单起见,我们将身份验证定义为“识别用户是谁”,将授权定义为“确定用户允许做什么和不允许做什么”。
基础知识
当客户端 连接 到 RabbitMQ 时,它们会指定一组凭据:用户名-密码对、JWT 令牌 或 x.509 证书。每个连接都有一个关联的用户,该用户已被身份验证。它还指向一个 虚拟主机,用户必须在该虚拟主机上拥有特定的一组权限。
用户凭据、目标虚拟主机和(可选)客户端 证书 在连接建立时指定。
有一对默认凭据,称为 默认用户。默认情况下,此用户只能 用于本地连接。使用默认用户的远程连接将被拒绝。
生产环境 不应使用默认用户。而是创建具有生成凭据的新用户帐户。
默认虚拟主机和用户
当服务器首次开始运行,并检测到其数据库未初始化或已被重置或删除(节点是“空白节点”)时,它将使用以下资源初始化一个新数据库:
- 一个名为
/(斜杠)的 虚拟主机 - 一个名为
guest的用户,默认密码为guest,拥有对/虚拟主机的所有访问权限
如果空白节点 在启动时导入定义,则不会创建此默认用户。
强烈建议 预先配置一个具有生成用户名和密码的新用户,或者 删除 guest 用户,或者至少 更改其密码 为一个不为人知的、相对安全的生成值。
身份验证:你是谁?
应用程序连接到 RabbitMQ 后,在执行操作之前,必须进行身份验证,即证明其身份。通过该身份,RabbitMQ 节点可以查找其权限并 授权 对诸如 虚拟主机、队列、交换器等资源的访问。
验证客户端的两种主要方式是 用户名/密码对 和 X.509 证书。用户名/密码对可与各种 身份验证后端 一起使用,这些后端会验证凭据。
身份验证失败的连接将被关闭,并在 服务器日志 中显示错误消息。
使用客户端 TLS (x.509) 证书数据进行身份验证
要使用 X.509 证书对客户端连接进行身份验证,必须启用内置插件 rabbitmq-auth-mechanism-ssl,并且客户端必须 配置为使用 EXTERNAL 机制。使用此机制,任何客户端提供的密码都将被忽略。
“guest”用户只能从 localhost 连接
默认情况下,禁止 guest 用户从远程主机连接;它只能通过回送接口(localhost)连接。这适用于 所有协议的连接。其他用户(默认情况下)不会受到此限制。
使用默认用户的远程连接将被拒绝,并在 日志中 显示类似如下的消息:
[error] <0.918.0> PLAIN login refused: user 'guest' can only connect via localhost
在生产系统中解决此问题的推荐方法是创建具有生成凭据的新用户,或一组用户,并授予其访问必需虚拟主机的权限。这可以通过 CLI 工具、HTTP API 或定义导入 来完成。
Fedora Linux 的 RabbitMQ 包 已 打补丁,允许使用已知凭据进行远程连接。
强烈建议不要采用此做法。Fedora Linux 用户应考虑从 Team RabbitMQ 的软件包存储库 安装 RabbitMQ,并避免使用发行版打包的版本,除非此严重的发行版特定安全漏洞已 得到解决。
这通过 配置文件 中的 loopback_users 项进行配置。
可以通过将 loopback_users 配置设置为 none 来允许 guest 用户从远程主机连接。
允许 guest 用户从远程主机连接的最小化 RabbitMQ 配置文件 如下所示:
# DANGER ZONE!
#
# allowing remote connections for default user is highly discouraged
# as it dramatically decreases the security of the system. Delete the default user
# instead and create a new one with generated secure credentials, or use JWT tokens,
# or x.509 certificates for clients to authenticate themselves
loopback_users = none
管理用户和权限
可以使用 CLI 工具 和定义导入(下文将介绍)来管理用户和权限。
开始之前:Shell 转义和生成的密码
生成包含非字母数字字符的复杂密码是一种常见的安全做法。此做法完全适用于 RabbitMQ 用户。
Shell(例如 bash、zsh 等)将某些字符(!、?、&、^、"、'、*、~ 等)解释为控制字符。
当在命令行中为 rabbitmqctl add_user、rabbitmqctl change_password 以及其他接受密码的命令指定密码时,必须为所使用的 shell 对这些控制字符进行适当的转义。不恰当的转义会导致命令失败,或者 RabbitMQ CLI 工具从 shell 接收到不同的值。
生成将在命令行上传递的密码时,长度较长(例如 40 到 100 个字符)且包含非常有限符号集(例如 :、=)的字母数字值是最安全的选择。
当通过 HTTP API 在不使用 shell 的情况下(例如使用 curl)创建用户时,不适用控制字符限制。但是,根据所使用的编程语言,可能需要不同的转义规则。
添加用户
要添加用户,请使用 rabbitmqctl add_user 或 rabbitmqadmin users declare。两者都有多种指定 密码 的方法。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
- cmd
# will prompt for password, only use this option interactively
rabbitmqctl add_user "username"
# Password is provided via standard input.
# Note that certain characters such as !, &, $, #, and so on must be escaped to avoid
# special interpretation by the shell.
echo '2a55f70a841f18b97c3a7db939b7adc9e34a0f1b' | rabbitmqctl add_user 'username'
# Password is provided as a command line argument.
# Note that certain characters such as !, &, $, #, and so on must be escaped to avoid
# special interpretation by the shell.
rabbitmqctl add_user 'username' '2a55f70a841f18b97c3a7db939b7adc9e34a0f1b'
# This example uses a salted password hash instead of a plaintext value.
# To produce a salted hash of a password, use
# 'rabbitmqctl hash_password "my-secret-password"'
rabbitmqctl add_user --pre-hashed-password 'username' '{value produced by "rabbitmqctl hash_password"}'
# Password is provided as a command line argument.
# Note that certain characters such as !, &, $, #, and so on must be escaped to avoid
# special interpretation by the shell.
rabbitmqadmin users declare \
--name 'username' \
--password '2a55f70a841f18b97c3a7db939b7adc9e34a0f1b'
# This exampel uses a salted password hash instead of a plaintext value.
# To produce a salted hash of a password, use
# 'rabbitmqadmin passwords salt_and_hash "my-secret-password"'
rabbitmqadmin users declare \
--name 'username' \
--password-hash "{value producted by 'rabbitmqadmin passwords salt_and_hash'}"
# password is provided as a command line argument
rabbitmqctl.bat add_user 'username' '9a55f70a841f18b97c3a7db939b7adc9e34a0f1d'
# passwords with special characters must be quoted correctly
rabbitmqctl.bat add_user 'username' '"w63pnZ&LnYMO(t"'
# This example uses a salted password hash instead of a plaintext value.
# To produce a salted hash of a password, use
# 'rabbitmqctl.bat hash_password "my-secret-password"'
rabbitmqctl.bat add_user --pre-hashed-password 'username' '{value produced by "rabbitmqctl.bat hash_password"}'
# Password is provided as a command line argument.
# Passwords with special characters must be quoted correctly.
rabbitmqadmin.exe users declare ^
--name "username" ^
--password "9a55f70a841f18b97c3a7db939b7adc9e34a0f1d"
# This exampel uses a salted password hash instead of a plaintext value.
# To produce a salted hash of a password, use
# 'rabbitmqadmin.exe passwords salt_and_hash "my-secret-password"'
rabbitmqadmin.exe users declare ^
--name "username" ^
--password-hash "{value producted by 'rabbitmqadmin.exe passwords salt_and_hash'}"
rem password is provided as a command line argument
rabbitmqctl.bat add_user "username" "9a55f70a841f18b97c3a7db939b7adc9e34a0f1d"
rem passwords with special characters must be quoted correctly
rabbitmqctl.bat add_user "username" "w63pnZ&LnYMO(t"
新添加的用户必须被 授予一个或多个虚拟主机的权限,否则其连接将被拒绝。
列出用户
要列出集群中的用户,请使用 rabbitmqctl list_users 或 rabbitmqadmin users list。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl list_users
rabbitmqadmin users list
rabbitmqctl.bat list_users
rabbitmqadmin.exe users list
删除用户
要删除用户,请使用 rabbitmqctl delete_user 或 rabbitmqadmin users delete。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl delete_user 'username'
rabbitmqadmin users delete --name 'username'
rabbitmqctl.bat delete_user 'username'
rabbitmqadmin.exe users delete --name "username"
授予用户权限
要在 虚拟主机 中为用户授予 权限,请使用 rabbitmqctl set_permissions 或 rabbitmqadmin declare permissions。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
# First ".*" for configure permission on every entity
# Second ".*" for write permission on every entity
# Third ".*" for read permission on every entity
rabbitmqctl set_permissions -p "custom-vhost" "username" ".*" ".*" ".*"
# Grant full permissions to a user in a virtual host
rabbitmqadmin declare permissions \
--vhost "custom-vhost" \
--user "username" \
--configure ".*" \
--write ".*" \
--read ".*"
# First ".*" for configure permission on every entity
# Second ".*" for write permission on every entity
# Third ".*" for read permission on every entity
rabbitmqctl.bat set_permissions -p 'custom-vhost' 'username' '.*' '.*' '.*'
# Grant full permissions to a user in a virtual host
rabbitmqadmin.exe declare permissions ^
--vhost "custom-vhost" ^
--user "username" ^
--configure ".*" ^
--write ".*" ^
--read ".*"
清除用户在虚拟主机中的权限
要撤销用户在 虚拟主机 中的 权限,请使用 rabbitmqctl clear_permissions 或 rabbitmqadmin delete permissions。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
# Revokes permissions in a virtual host
rabbitmqctl clear_permissions -p "custom-vhost" "username"
# Revokes permissions from a user in a virtual host
rabbitmqadmin delete permissions \
--vhost "custom-vhost" \
--user "username"
# Revokes permissions in a virtual host
rabbitmqctl.bat clear_permissions -p 'custom-vhost' 'username'
# Revokes permissions from a user in a virtual host
rabbitmqadmin.exe delete permissions ^
--vhost "custom-vhost" ^
--user "username"
为新虚拟主机预先配置默认用户权限
可以在 rabbitmq.conf 中预先配置默认用户权限,以便在创建名称与模式匹配的新虚拟主机时自动授予权限。
## Grants user 'monitoring' access to all virtual hosts
## with certain permissions
default_users.monitoring.vhost_pattern = .*
default_users.monitoring.tags = monitoring
default_users.monitoring.configure = ^$
default_users.monitoring.read = .*
default_users.monitoring.write = ^$
此功能仅应用于为服务帐户(如监控和自动化工具)授予虚拟主机访问权限。
对多个虚拟主机进行操作
每个 rabbitmqctl 和 rabbitmqadmin 权限管理操作都作用于单个虚拟主机。批量操作需要脚本化,虚拟主机列表来自 rabbitmqctl list_vhosts --silent 或 rabbitmqadmin vhosts list。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
# Assumes a Linux shell.
# Grants a user permissions to all virtual hosts.
for v in $(rabbitmqctl list_vhosts --silent); do rabbitmqctl set_permissions -p $v "a-user" ".*" ".*" ".*"; done
# Assumes a Linux shell.
# Grants a user permissions to all virtual hosts using rabbitmqadmin.
# Note: this example assumes rabbitmqadmin vhosts list outputs one vhost name per line
for v in $(rabbitmqadmin vhosts list --quiet | awk '{print $1}' | tail -n +2); do
rabbitmqadmin declare permissions --vhost "$v" --user "a-user" --configure ".*" --write ".*" --read ".*"
done
rabbitmqctl.bat list_vhosts --silent | %{ rabbitmqctl.bat set_permissions -p $_ 'a-user' '.*' '.*' '.*' }
# Grants a user permissions to all virtual hosts using rabbitmqadmin
# This approach uses rabbitmqctl for vhost listing as it's more straightforward for scripting
rabbitmqctl.bat list_vhosts --silent | %{ rabbitmqadmin.exe declare permissions --vhost $_ --user 'a-user' --configure '.*' --write '.*' --read '.*' }
播种(预创建)用户和权限
生产环境 通常需要预先配置(播种)多个虚拟主机、用户和用户权限。
这可以通过以下几种方式完成:
- 使用 CLI 工具
- 定义导出并在节点启动时导入(推荐)
- 在配置文件中覆盖 默认凭据
CLI 工具
请参阅 用户管理 部分。
节点启动时导入定义
此过程包括以下步骤:
- 设置一个临时节点,并使用 CLI 工具创建必要的虚拟主机、用户、权限等。
- 将定义导出到定义文件。
- 删除文件中不相关的部分。
- 配置节点在节点启动时或之后导入文件。
请参阅定义指南中的 在节点启动时导入定义 以了解更多信息。
节点启动后导入定义
请参阅定义指南中的 在节点启动后导入定义。
覆盖默认用户凭据
可以使用两个配置选项来覆盖默认用户凭据。此用户仅在首次节点启动时创建,因此必须在首次启动之前存在于配置文件中。
设置项为:
# default is "guest", and its access is limited to localhost only.
# See ./access-control#default-state
default_user = a-user
# default is "guest"
default_pass = 768a852ed69ce916fa7faa278c962de3e4275e5f
与 rabbitmq.conf 中的所有值一样,# 字符表示注释的开始,因此在生成的凭据中必须避免使用此字符。
默认用户凭据也可以被加密。此主题在 配置值加密 中有更详细的介绍。
授权:权限如何工作
当 RabbitMQ 客户端建立与服务器的连接并 进行身份验证 时,它会指定一个它打算在其操作的虚拟主机。此时会强制执行第一级访问控制,服务器会检查用户是否拥有访问虚拟主机的任何权限,否则将拒绝连接尝试。
资源,即交换器和队列,是特定虚拟主机内的命名实体;相同的名称在每个虚拟主机中表示不同的资源。在对资源执行特定操作时,会强制执行第二级访问控制。
RabbitMQ 区分对资源的 configure、write 和 read 操作。configure 操作会创建或销毁资源,或更改其行为。write 操作会将消息注入到资源中。read 操作会从资源中检索消息。
为了对资源执行操作,用户必须已被授予相应的权限。
- AMQP 0-9-1
- AMQP 1.0
- MQTT
下表显示了对所有执行权限检查的 AMQP 0-9-1 命令,在什么类型的资源上需要什么权限。
| AMQP 0-9-1 操作 | 配置 | write | read | |
|---|---|---|---|---|
| exchange.declare | (passive=false) | exchange | ||
| exchange.declare | (passive=true) | |||
| exchange.declare | (带 AE) | exchange | 交换器 (AE) | exchange |
| exchange.delete | exchange | |||
| queue.declare | (passive=false) | queue | ||
| queue.declare | (passive=true) | |||
| queue.declare | (带 DLX) | queue | 交换器 (DLX) | queue |
| queue.delete | queue | |||
| exchange.bind | 交换器 (目标) | 交换器 (源) | ||
| exchange.unbind | 交换器 (目标) | 交换器 (源) | ||
| queue.bind | queue | exchange | ||
| queue.unbind | queue | exchange | ||
| basic.publish | exchange | |||
| basic.get | queue | |||
| basic.consume | queue | |||
| queue.purge | queue |
下表显示了对所有执行权限检查的 AMQP 1.0 命令,在什么类型的资源上需要什么权限。
| AMQP 操作 | 配置 | write | read | |
|---|---|---|---|---|
| 声明交换器 | exchange | |||
| 声明交换器 | 带 AE | exchange | 交换器 (AE) | exchange |
| 删除交换器 | exchange | |||
| 将交换器绑定到交换器 | 交换器 (目标) | 交换器 (源) | ||
| 将交换器从交换器解绑 | 交换器 (目标) | 交换器 (源) | ||
| 声明队列 | queue | |||
| 声明队列 | 带 DLX | queue | 交换器 (DLX) | queue |
| 删除队列 | queue | |||
| 清除队列 | queue | |||
| 将队列绑定到交换器 | queue | exchange | ||
| 将队列从交换器解绑 | queue | exchange | ||
| 获取队列 | ||||
| 附加 (发送方) | 交换器地址 | exchange | ||
| 附加 (发送方) | 队列地址 | 交换器 (amq.default) | ||
| 附加 (接收方) | queue |
权限是以三个正则表达式的组合形式表示的——每个用于配置、写入和读取——按虚拟主机进行。用户被授予对所有名称与正则表达式匹配的资源的操作的相应权限。
例如,上表显示 queue.bind 协议操作需要对目标 queue 的 write 权限,对目标 exchange 需要 read 权限。
换句话说,为了允许用户将名为 queueA 的队列绑定到名为 exchangeB 的交换器,用户将需要(针对正确的虚拟主机)write 权限正则表达式来匹配 queueA,以及 read 权限正则表达式来匹配 exchangeB。
为了方便起见,RabbitMQ 在执行权限检查时会将 AMQP 0-9-1 的默认交换器的空白名称映射到 'amq.default'。
正则表达式 '^$',即只匹配空字符串,涵盖所有资源,并有效地阻止用户执行任何操作。内置的 AMQP 0-9-1 资源名称以 amq. 开头,服务器生成的名称以 amq.gen 开头。
例如,'^(amq\.gen.*|amq\.default)$' 使用户可以访问服务器生成的名称和默认交换器。空字符串 '' 是 '^$' 的同义词,并以完全相同的方式限制权限。
RabbitMQ 可能会在每个连接或每个通道的基础上 缓存 访问控制检查的结果。因此,用户权限的更改可能仅在用户重新连接时生效。
有关如何设置访问控制的详细信息,请参阅 用户管理 部分以及 rabbitmqctl man page。
用户标签和管理 UI 访问
除了上面介绍的权限外,用户还可以关联标签。目前只有管理 UI 访问由用户标签控制。
标签使用 rabbitmqctl 进行管理。新创建的用户默认没有设置任何标签。
请参阅 管理插件指南 以了解支持哪些标签以及它们如何限制管理 UI 访问。
主题授权
RabbitMQ 支持 topic 交换器的 topic 授权。发布到 topic 交换器的消息的路由键在强制执行发布授权时被考虑(例如,在 RabbitMQ 默认授权后端中,路由键与正则表达式匹配以决定消息是否可以向下路由)。Topic 授权针对 STOMP 和 MQTT 等协议,这些协议围绕 topic 构建并底层使用 topic 交换器。
Topic 授权是现有发布者检查的附加层。发布消息到 topic 类型交换器将经过 basic.publish 和路由键检查。后者仅在前一个拒绝访问时才会被调用。
Topic 授权也可以对 topic 消费者强制执行。请注意,它对不同协议的工作方式不同。Topic 授权的概念仅对 MQTT 和 STOMP 等面向 topic 的协议有意义。例如,在 AMQP 0-9-1 中,消费者从队列消费,因此适用标准的资源权限。此外,对于 AMQP 0-9-1,AMQP 0-9-1 topic 交换器和队列/交换器之间的绑定路由键将根据配置的 topic 权限(如果有)进行检查。有关 RabbitMQ 如何处理 topic 授权的更多信息,请参阅 STOMP 和 MQTT 文档指南。
当使用默认授权后端时,发布到 topic 交换器或从 topic 消费始终被授权,如果没有定义 topic 权限(在新安装的 RabbitMQ 中就是这种情况)。使用此授权后端,topic 授权是可选的:您不需要批准任何交换器。因此,要使用 topic 授权,您需要选择加入并为一或多个交换器定义 topic 权限。有关详细信息,请参阅 rabbitmqctl man page。
内部(默认)授权后端支持权限模式中的变量展开。支持三个变量:username、vhost 和 client_id。请注意,client_id 仅适用于 MQTT。例如,如果 tonyg 是已连接的用户,则权限 ^{username}-.* 将展开为 ^tonyg-.*。
如果使用不同的授权后端(例如 LDAP、HTTP、OAuth 2),请参阅这些后端的文档。
如果使用自定义授权后端,topic 授权将通过实现 rabbit_authz_backend 行为的 check_topic_access 回调来强制执行。
撤销用户访问和凭据轮换
撤销用户访问
要撤销用户访问,推荐的方法是 删除用户。属于被删除用户的所有打开的连接都将被关闭。
也可以清除用户权限,但这不会影响任何当前打开的连接。连接使用授权操作缓存,因此客户端操作最终会被拒绝。时间长度取决于所使用的 授权后端。
凭据轮换
存储在内部数据存储中的用户凭据的凭据轮换通常涉及以下步骤:
- 使用 CLI 工具更改用户密码 或通过
PUT /api/users/{user}HTTP API 端点更新它。 - 使用
rabbitmqctl close_all_user_connections或rabbitmqctl close_all_connections关闭所有现有连接。 - 确保应用程序可以发现新凭据并重新连接,例如,通过重新启动它们。
如果怀疑凭据泄露,可以采用更彻底的凭据轮换程序。
- 评估用户的权限并 获取完整的集群定义文件。
- 删除用户,从而撤销其访问权限并关闭所有使用这些凭据的现有连接。
- 用新的生成密码重新添加用户。
- 重新授予用户对其先前拥有访问权限的所有虚拟主机的访问权限。
- 确保应用程序可以发现新凭据并重新连接,例如,通过重新启动它们。
使用外部 身份验证后端,如 LDAP,用户帐户在 RabbitMQ 外部管理,因此凭据轮换例程也将位于 RabbitMQ 外部。
身份验证和授权后端
身份验证和授权是可插拔的。插件可以提供以下实现的后端:
- 身份验证(“authn”)后端:它们确定客户端身份并决定是否允许客户端连接。
- 授权(“authz”)后端:它们确定已识别(已身份验证)的客户端是否有权执行特定操作。
插件可以同时提供这两个后端,并且很常见。RabbitMQ 附带以下 内置插件,它们提供身份验证和授权后端:
以下内置插件提供授权后端实现:
某些插件,如 Source IP range one,也仅提供授权后端。
身份验证应由内部数据库、LDAP 等处理。
可以使用特殊的 缓存后端 与其他后端 结合使用,以显著减少它们对 LDAP 或 HTTP 服务器等外部服务的负载。
组合后端
可以使用 auth_backends 配置键为 authn 或 authz 使用多个后端。当使用多个身份验证后端时,链中后端返回的第一个正面结果被认为是最终结果。这不应与混合后端混淆(例如,使用 LDAP 进行身份验证和内部后端进行授权)。
以下示例配置 RabbitMQ 仅使用内部后端(这也是默认设置):
# rabbitmq.conf
#
# 1 here is a backend name. It can be anything.
# Since we only really care about backend
# ordering, we use numbers throughout this guide.
#
# "internal" is an alias for rabbit_auth_backend_internal
auth_backends.1 = internal
上面的示例使用了别名 internal 来表示 rabbit_auth_backend_internal。以下别名可用:
internalforrabbit_auth_backend_internalldapforrabbit_auth_backend_ldap(来自 LDAP 插件)oauth或oauth2forrabbit_auth_backend_oauth2(来自 OAuth 2.0 插件)httpforrabbit_auth_backend_http(来自 HTTP auth backend 插件)dummyforrabbit_auth_backend_dummy
对于没有快捷方式的插件,必须使用完整的模块名(而不是插件的名称)。
# note that the module name begins with a "rabbit_", not "rabbitmq_", like plugin
# names usually do
auth_backends.1 = rabbit_auth_backend_ip_range
使用第三方插件时,提供完整的模块名是必要的。
以下示例配置 RabbitMQ 将 LDAP 后端 用于身份验证和授权。内部数据库将不会被查询。
auth_backends.1 = ldap
这将首先检查 LDAP,然后在用户无法通过 LDAP 进行身份验证时回退到内部数据库。
auth_backends.1 = ldap
auth_backends.2 = internal
与上面相同,但会回退到 HTTP 后端,而不是。
# rabbitmq.conf
#
auth_backends.1 = ldap
# uses module name instead of a short alias, "http"
auth_backends.2 = rabbit_auth_backend_http
# See HTTP backend docs for details
auth_http.user_path = http://my-authenticator-app/auth/user
auth_http.vhost_path = http://my-authenticator-app/auth/vhost
auth_http.resource_path = http://my-authenticator-app/auth/resource
auth_http.topic_path = http://my-authenticator-app/auth/topic
以下示例配置 RabbitMQ 将内部数据库用于身份验证,并将 源 IP 范围后端 用于授权。
# rabbitmq.conf
#
auth_backends.1.authn = internal
# uses module name because this backend is from a 3rd party
auth_backends.1.authz = rabbit_auth_backend_ip_range
以下示例配置 RabbitMQ 将 LDAP 后端 用于身份验证,并将内部后端用于授权。
# rabbitmq.conf
#
auth_backends.1.authn = ldap
auth_backends.1.authz = internal
下面的示例相当高级。它将首先检查 LDAP。如果在 LDAP 中找到用户,则密码将与 LDAP 进行比对,然后后续的授权检查将在内部数据库上进行(因此 LDAP 中的用户也必须存在于内部数据库中,但不需要密码)。如果用户未在 LDAP 中找到,则将尝试仅使用内部数据库进行第二次尝试。
# rabbitmq.conf
#
auth_backends.1.authn = ldap
auth_backends.1.authz = internal
auth_backends.2 = internal
身份验证机制
RabbitMQ 支持多种 SASL 身份验证机制。服务器内置四种这样的机制:PLAIN、AMQPLAIN、ANONYMOUS 和 RABBIT-CR-DEMO,以及一种 — EXTERNAL — 作为 插件 提供。
可以通过插件提供更多身份验证机制。有关一般插件开发的更多信息,请参阅 插件开发指南。
内置身份验证机制
内置机制包括:
| 机制 | 描述 |
| PLAIN | SASL PLAIN 身份验证。这在 RabbitMQ 服务器和客户端中默认启用,并且是大多数其他客户端的默认设置。 |
| AMQPLAIN | 非标准的 PLAIN 版本,为向后兼容而保留。这在 RabbitMQ 服务器中默认启用。 |
| ANONYMOUS | 此机制默认启用,允许匿名客户端在不提供任何凭据的情况下连接。RabbitMQ 将使用 |
| EXTERNAL | 身份验证使用带外机制进行,例如 x509 证书对等验证、客户端 IP 地址范围或类似机制。此类机制通常由 RabbitMQ 插件提供。 |
| RABBIT-CR-DEMO | 非标准机制,用于演示挑战-响应身份验证。此机制的安全性等同于 PLAIN,并且在 RabbitMQ 服务器中不默认启用。 |
服务器中的机制配置
auth_mechanisms 配置键决定了已安装的哪些机制会被提供给连接的客户端。
此变量应为对应于机制名称的接受值列表,例如,以下列表
auth_mechanisms.1 = PLAIN
auth_mechanisms.2 = AMQPLAIN
auth_mechanisms.3 = ANONYMOUS
是默认使用的。
服务器机制按优先级别递减的顺序排列。请参阅 配置文件 文档。
客户端中的机制配置
应用程序必须选择加入才能使用不同的身份验证机制,例如 EXTERNAL。
Java 中的机制配置
Java 客户端默认不使用 javax.security.sasl 包,因为这在非 Oracle JDK 上可能不稳定,并且在 Android 上完全缺失。有一个 RabbitMQ 特定的 SASL 实现,由 SaslConfig 接口配置。提供了 DefaultSaslConfig 类以在常见情况下使 SASL 配置更方便。提供了 JDKSaslConfig 类以作为到 javax.security.sasl 的桥梁。
ConnectionFactory.getSaslConfig() 和 ConnectionFactory.setSaslConfig(SaslConfig) 是与身份验证机制交互的主要方法。
.NET 中的机制配置
.NET 客户端基于 AuthMechanism 和 AuthMechanismFactory 接口提供自己的 SASL 机制实现。ConnectionFactory.AuthMechanisms 属性是优先顺序的身份验证机制工厂列表。
Erlang 中的机制配置
Erlang 客户端在其 amqp_auth_mechanisms 模块中提供自己的 SASL 机制实现。#amqp_params 记录可以提供一个按优先顺序排列的网络连接身份验证函数列表。
身份验证故障排除
本节涵盖几个与身份验证相关的非常常见的问题。有关授权(权限)错误,请参阅下面的另一个部分。
检查 服务器日志 在调查身份验证相关问题时至关重要。
错误的权限
身份验证尝试失败,即客户端提供了错误的凭据,将导致 服务器日志消息 如下所示:
2019-03-25 12:28:19.047 [info] <0.1613.0> accepting AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672)
2019-03-25 12:28:19.056 [error] <0.1613.0> Error on AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672, state: starting):
PLAIN login refused: user 'user2' - invalid credentials
2019-03-25 12:28:22.057 [info] <0.1613.0> closing AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672)
使用 X.509 证书 进行身份验证 的连接上的身份验证失败将以不同的方式记录。有关详细信息,请参阅 TLS 故障排除指南。
rabbitmqctl authenticate_user 可用于测试用户名和密码对的身份验证。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqctl (PowerShell)
rabbitmqctl authenticate_user "a-username" "a/password"
# note that double quotes are required due to the & character
rabbitmqctl.bat authenticate_user 'a-username' '"a/p&assword"'
如果身份验证成功,它将以代码零退出。失败时,将使用非零退出代码并打印失败错误消息。
rabbitmqctl authenticate_user 将使用 CLI 到节点的通信连接来尝试将用户名/密码对身份验证到内部 API 端点。假定连接是受信任的。如果不是这种情况,其流量可以 使用 TLS 加密。
默认用户的远程主机连接被拒绝
默认用户无法从远程主机连接 是有原因的。启用此类连接可能会显着降低集群的安全性。请考虑为所有生产集群 创建具有生成凭据的唯一用户。
如果凭据报告正确,则从 localhost 的连接成功但远程连接失败,问题是 默认用户连接限制。
在这种情况下,错误将如下所示:
2024-08-24 17:28:32.153698-04:00 [error] <0.1567.0> PLAIN login refused: user 'guest' can only connect via localhost
必须为从远程主机连接的客户端(包括 shovels 和 federation links) 创建 一个具有生成凭据的单独用户。
AMQP 0-9-1 中的身份验证失败通知
根据 AMQP 0-9-1 规范,身份验证失败应导致服务器立即关闭 TCP 连接。但是,通过 RabbitMQ 客户端可以选择接收更具体的通知,使用 AMQP 0-9-1 的 身份验证失败通知 扩展。现代客户端库会以对用户透明的方式支持该扩展:不需要配置,身份验证失败将导致可见的返回错误、异常或其他在特定编程语言或环境中使用的通信问题的方式。
授权故障排除
检查 服务器日志 在调查授权相关问题时至关重要。
缺少权限
授权失败最常见的情况是,用户已被创建但未被 授予 客户端尝试连接的虚拟主机的权限。
在这种情况下,连接将被拒绝,并显示类似如下的消息:
2019-03-25 12:26:16.301 [info] <0.1594.0> accepting AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672)
2019-03-25 12:26:16.309 [error] <0.1594.0> Error on AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672, user: 'user2', state: opening):
access to vhost '/' refused for user 'user2'
2019-03-25 12:26:16.310 [info] <0.1594.0> closing AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672, vhost: 'none', user: 'user2')
权限不足
另一个非常常见的情况是,权限已定义但 不足以执行客户端尝试执行的操作。
在这种情况下,连接将被接受:
可以使用 rabbitmqctl list_permissions 和 rabbitmqadmin users permissions 来检查用户在给定虚拟主机中的权限。
- 使用 rabbitmqctl (bash)
- 使用 rabbitmqadmin (bash)
- 使用 rabbitmqctl (PowerShell)
- 使用 rabbitmqadmin (PowerShell)
rabbitmqctl list_permissions --vhost /
# => Listing permissions for vhost "/" ...
# => user configure write read
# => user2 .* .* .*
# => guest .* .* .*
# => temp-user .* .* .*
rabbitmqctl list_permissions --vhost gw1
# => Listing permissions for vhost "gw1" ...
# => user configure write read
# => guest .* .* .*
# => user2 ^user2 ^user2 ^user2
# List permissions for all users in the default virtual host
rabbitmqadmin users permissions --vhost /
# List permissions for all users in a specific virtual host
rabbitmqadmin users permissions --vhost gw1
rabbitmqctl.bat list_permissions --vhost /
rabbitmqctl.bat list_permissions --vhost gw1
# List permissions for all users in the default virtual host
rabbitmqadmin.exe users permissions --vhost "/"
# List permissions for all users in a specific virtual host
rabbitmqadmin.exe users permissions --vhost "gw1"
授权失败(权限违规)将使用以下消息记录:
2019-03-25 12:30:05.209 [error] <0.1627.0> Channel error on connection <0.1618.0> (127.0.0.1:63881 -> 127.0.0.1:5672, vhost: 'gw1', user: 'user2'), channel 1:
operation queue.declare caused a channel exception access_refused: access to queue 'user3.q1' in vhost 'gw1' refused for user 'user2'