安全配置方案
从 6.3 版本开始,EMQX 支持节点级的安全配置方案(Security Profile)。安全配置方案决定一组与安全相关的默认行为。EMQX 提供两种方案:
legacy(默认):保持早期 EMQX 版本的默认行为。hardened:采用更严格的安全默认行为。
EMQX 计划从 7.0 版本开始默认使用 hardened。首次部署时,建议使用 hardened。将部署从 legacy 迁移到 hardened 前,请评估下文列出的行为变更。
选择方案
安全配置方案由环境变量 EMQX_SECURITY_PROFILE 选择。请在 emqx.env 文件中设置该变量,emqx 命令的每次调用都会加载此文件:
- rpm 和 deb 安装:
/etc/emqx/emqx.env - Docker 镜像:
/opt/emqx/etc/emqx.env - tar.gz 安装:
etc/emqx.env
取消文件中 EMQX_SECURITY_PROFILE 行的注释并设置值:
EMQX_SECURITY_PROFILE=hardened然后重启节点。
emqx.env 中的值会覆盖从环境继承的变量,包升级会保留对该文件的修改。也可以直接在环境中设置该变量,例如使用 docker run -e EMQX_SECURITY_PROFILE=hardened,或在前台启动前执行 export EMQX_SECURITY_PROFILE=hardened。
EMQX 在启动时读取该变量一次,读取发生在解析配置文件之前,因此无法在配置文件中设置安全配置方案。有效值为 legacy、hardened 或空(使用默认值)。其他值会导致节点启动失败。集群中的每个节点应设置相同的值。
TIP
安全配置方案只改变默认行为。下文列出的大多数行为也可以单独配置,与所选方案无关。
查看当前安全配置方案
在 Dashboard 中,点击监控 -> 集群概览 -> 节点,查看安全配置模式列。该列显示各节点启动时使用的 legacy 或 hardened 方案。该值在启动时确定,只有在节点重启后才可能改变。
对比所有运行中节点的值,确认集群使用一致的安全配置方案。对于停止的节点和运行 6.3 之前版本的节点,该列不显示值。
hardened 方案的行为变更
与 legacy 相比,hardened 方案改变以下行为。
节点和集群安全
- 拒绝已知的不安全 Erlang cookie。 如果节点使用内置的默认 Erlang cookie 或常用示例值
emqxsecretcookie,节点将无法启动。启动 EMQX 前,请配置非默认的node.cookie或设置EMQX_NODE__COOKIE。集群中的所有节点必须使用相同的 cookie。
监听器暴露
hardened 方案采用以下默认绑定地址。您可以通过节点级配置项 node.default_listener_address 覆盖这些默认值,该配置项用于统一设置节点上未显式指定绑定地址的监听器:
- MQTT 监听器默认绑定回环地址。 MQTT TCP、SSL、WebSocket、安全 WebSocket 和 QUIC 监听器在
bind省略或仅指定端口时,只监听回环接口。配置显式的绑定地址(例如bind = "0.0.0.0:1883")以接受外部连接。 - Dashboard HTTP 监听器默认绑定回环地址。 Dashboard HTTP 监听器在
bind省略或仅指定端口时,只监听回环接口。配置显式的绑定地址以接受外部连接。
该配置项也适用于网关监听器,但安全配置方案不会改变网关监听器的默认绑定地址。支持的取值及配置方法参见默认监听地址。
认证
- 必须显式配置认证。 当没有配置认证器,或所有认证器都被禁用时,客户端将被拒绝。要在某个监听器上显式允许匿名访问,设置该监听器的
enable_authn = false。 - 认证后端故障将拒绝访问。 发生认证器后端错误、后端响应格式错误或认证器前置条件求值错误,或者 JWT 验证密钥不可用时,EMQX 将拒绝客户端,而不是继续尝试下一个认证器。设置
authentication_settings.ignore_backend_failures = true可允许回退到后续认证器。 - JWT 认证器不忽略缺失的 JWT。 客户端未提供配置的 JWT 字段时,JWT 认证器将拒绝该客户端。设置认证器的
on_missing_jwt = ignore可允许这些客户端继续尝试下一个认证器。 - 在混合认证链中,非 JWT 凭据必须跳过 JWT 认证器。 JWT 认证器收到格式错误的 JWT 时,认证结果为失败。当 JWT 认证器和后续的密码认证器从同一字段(例如
password)读取 JWT 或密码时,设置 JWT 认证器的precondition = "is_jwt(password)",使普通密码继续交给下一个认证器处理。 - 验证 JWKS 出站 TLS。 JWT 认证器从 JWKS HTTPS 端点获取密钥时会验证对端证书和主机名。使用不受信任证书的端点将不可用。在特定 JWKS 端点上设置
ssl.verify = verify_none可禁用验证。
授权
- 授权后端故障将拒绝操作。 授权后端错误、规则格式错误和模板求值错误会直接拒绝发布或订阅操作,而不是继续尝试后续数据源或回退到未匹配规则时的处理逻辑。设置
authorization.ignore_backend_failures = true可忽略后端故障并继续下一个授权数据源。 - 授权主题模板替换值中的禁用字符将拒绝操作。 默认情况下,替换值包含
/、+或#时,该规则在legacy方案下视为未匹配,在hardened方案下直接拒绝操作。例如,客户端 ID 为i/am/+/good/#的客户端匹配规则{allow, all, all, ["t/${clientid}/#"]}.时即属于这种情况。可通过authorization.topic_template_allow.slash、authorization.topic_template_allow.plus或authorization.topic_template_allow.hash单独放行对应字符。 - 默认文件授权数据源变为默认拒绝。 默认
acl.conf的最后一条规则是{allow, {security_profile, legacy}}.,该规则在legacy方案下允许操作,在hardened方案下不生效。在hardened方案下,未匹配任何规则的操作将落入authorization.no_match,其默认值为deny。将最后一条规则改为{allow, all}.可恢复宽松行为。{security_profile, legacy}和{security_profile, hardened}条件可用于acl.conf中的任意规则(包括在and和or表达式中),使自定义规则仅在所选方案下生效。 - 内部订阅需要授权。 自动订阅(Auto Subscribe)等功能发起的订阅会经过主题验证、授权检查、MQTT 能力检查和订阅钩子。特权管理类的强制订阅操作仍然绕过 MQTT 授权。
延迟发布
- 重放延迟消息时重新授权。 EMQX 使用调度消息时保存的授权上下文,根据重放时的发布授权规则和封禁记录重新检查消息。调度时已通过授权的消息可能在重放时被丢弃。
重要提示
在 hardened 方案下,EMQX 会丢弃升级前创建的待处理延迟消息,因为这些消息不包含授权上下文。legacy 方案仍会重放这些消息。
扩展
- 访问控制钩子故障将拒绝请求。 认证或授权钩子抛出的异常会中断处理并拒绝请求。这对插件或 ExHook 扩展提供的自定义认证和授权尤其重要。
- ExHook
message.publish失败将拒绝发布。 如果没有可用的 ExHook 服务端,或者failed_action为deny的 ExHook 服务端在处理message.publish时失败,EMQX 将阻止消息发布。在legacy方案下,相同的失败不会阻止消息发布。 - 安装插件必须提供安装包摘要。
emqx ctl plugins allow <Name-Vsn>必须带上sha256:<hex>参数。该授权将插件安装包绑定到指定摘要,只有字节内容与摘要一致的上传才会被安装。未携带摘要的授权将被拒绝,包括集群中其他节点发来的授权。在legacy方案下,该参数仍为可选。
Dashboard
- 不接受 Dashboard 默认凭据。 使用默认密码
public的本地 Dashboard 账户无法登录,包括升级前创建的管理员账户。切换到hardened方案前请先修改密码。
默认监听地址
配置项 node.default_listener_address 用于设置未指定地址的监听器绑定地址,即仅指定端口的绑定(例如 bind = 1883)。该配置适用于 MQTT 监听器、网关监听器和 Dashboard HTTP 监听器。显式的 IP:port 绑定始终优先。
EMQX 在各节点本地确定默认地址,并在监听器启动时应用该地址。配置中的 bind 值保持不变:仅指定端口的绑定不会被持久化为 IP:port。因此,使用相同监听器配置的节点可能监听不同的地址。
使用该配置项可以独立于安全配置方案控制监听器的暴露范围。例如,若要保持 hardened 方案,并将使用默认地址的监听器绑定到所有网络接口,可在节点的 emqx.conf 中添加:
node.default_listener_address = "all"有效值:
| 值 | 绑定地址 |
|---|---|
loopback | 127.0.0.1。当 Dashboard 启用 inet6 选项时,改为绑定 ::1。 |
nodename | Erlang 节点名中 @ 之后的主机部分。如果它是 IP 地址,EMQX 直接绑定该地址;否则在启动时解析该名称,绑定解析出的第一个 IPv4 地址;若没有 IPv4 地址,则绑定第一个 IPv6 地址。 |
all | 所有网络接口。默认 IPv4 配置下,地址为 0.0.0.0。地址族由监听器配置决定。 |
| IP 地址 | 指定的地址,例如 192.168.1.10 或 ::1。在大多数系统上 :: 同时接受 IPv4 和 IPv6 连接,具体由操作系统的 bindv6only 设置决定。 |
| 主机名 | 在启动时解析,例如 broker1.example.com。 |
未设置该配置项时,由安全配置方案决定 MQTT 监听器和 Dashboard HTTP 监听器的默认地址:legacy 绑定所有接口,hardened 绑定回环地址。网关监听器在两种方案下均绑定所有接口。
该配置项为节点级配置。EMQX 在启动时读取一次,因此修改后需要重启节点。也可以通过环境变量 EMQX_NODE__DEFAULT_LISTENER_ADDRESS 设置。
TIP
当环境变量 EMQX_NODE__DEFAULT_LISTENER_ADDRESS 未设置或为空时,官方 Docker 镜像的入口脚本将其设置为 all,因为容器的回环接口无法通过发布的端口访问。在此默认设置下,仅指定端口的监听器在两种方案下均监听所有网络接口,从而允许通过发布的容器端口访问。如需覆盖此默认值,请显式将该环境变量设置为其他支持的值。显式指定 IP 地址的监听器绑定保持不变。
跨安全配置方案恢复备份
数据备份会记录导出该备份的节点所使用的安全配置方案。将安全配置方案记录为 legacy 的备份,或不包含该元数据的备份导入 hardened 节点时,EMQX 默认拒绝导入。导入 legacy 节点不受影响。
覆盖此保护机制前,请评估两种方案之间的行为差异。有关兼容性规则和覆盖方法,参见备份与恢复。
滚动升级
集群中的所有节点必须使用相同的安全配置方案。节点之间方案不一致时,访问控制结果将取决于客户端连接到哪个节点。运行 6.3 之前版本的节点始终按 legacy 方案运行。
从 6.3 之前的版本执行滚动升级时:
- 不要在已升级的节点上设置
EMQX_SECURITY_PROFILE=hardened。保持该变量为空,或将其设置为legacy,使已升级节点与仍在运行旧版本的节点行为一致。 - 在所有节点上完成滚动升级。
- 之后再按照下文的迁移步骤将集群切换到
hardened。
迁移
将已有部署从 legacy 迁移到 hardened:
- 逐项评估上述行为变更,在严格默认值不适用的地方应用显式配置。
- 配置非默认的 Erlang cookie,并确认集群中的每个节点使用相同的值。
- 确认需要接受外部连接的监听器和 Dashboard 已配置显式绑定地址,或通过
node.default_listener_address统一设置所有使用默认地址的监听器。 - 确认每个节点都已配置认证,或在需要的地方显式启用匿名访问。
- 修改仍在使用默认密码的 Dashboard 账户。
- 升级后启用
hardened前,等待升级前创建的待处理延迟消息完成重放;否则,应接受 EMQX 将丢弃这些消息。 - 在所有节点上设置
EMQX_SECURITY_PROFILE=hardened,然后逐个重启节点。
要保持原有行为,设置 EMQX_SECURITY_PROFILE=legacy 或不设置该变量。