EMQX AWS KMS + Secrets Manager + 内存文件系统完整方案

1. Dashboard 上传证书的存储位置

当你在 EMQX Dashboard 中粘贴或上传证书内容时,这些数据不会 以文件形式写入 /etc/emqx/certs/ 或任何磁盘目录。

  • 存储位置 :证书内容(包括私钥)被序列化后存储在 EMQX 的内置 Mnesia/RocksDB 数据库中,作为集群配置数据的一部分。
  • 运行时加载:EMQX 在启动监听器或刷新配置时,直接从内存中的配置树读取证书 PEM 字符串,并传递给 Erlang/OTP 的 SSL 模块,全程不落盘。
  • 与静态文件的区别/etc/emqx/certs/ 下的文件仅用于 emqx.conf 中通过文件路径引用的场景。Dashboard 配置的证书与这些文件完全独立,互不覆盖。

⚠️ 安全提示:这意味着通过 Dashboard 上传的私钥安全性依赖于 EMQX 内部数据库的访问控制。确保 Dashboard API 启用了强认证,且数据库目录权限受严格保护。


2. AWS KMS + Secrets Manager + 内存文件系统完整方案

架构概览
复制代码
AWS KMS (加密密钥)
    ↓ 信封加密
AWS Secrets Manager (存储加密后的 key.pem)
    ↓ 启动时拉取 + 解密
Init Script / Sidecar
    ↓ 写入
tmpfs 内存文件系统 (/run/emqx-secrets/)
    ↓ 文件路径引用
EMQX (emqx.conf 静态配置)
步骤一:准备 KMS 密钥并加密私钥存入 Secrets Manager
复制代码
# 1. 创建 KMS 密钥(记录 KeyId)
KEY_ID=$(aws kms create-key --description "EMQX TLS Key" \
  --query 'KeyMetadata.KeyId' --output text)

# 2. 将 key.pem 加密后存入 Secrets Manager
aws secretsmanager create-secret \
  --name emqx/tls/server-key \
  --description "EMQX Server TLS Private Key" \
  --secret-string file:///etc/emqx/certs/key.pem \
  --kms-key-id "$KEY_ID"

💡 如果 Secret 已存在,使用 update-secret 代替 create-secret

步骤二:创建 tmpfs 挂载点
复制代码
# 创建专用目录
mkdir -p /run/emqx-secrets
chown emqx:emqx /run/emqx-secrets
chmod 700 /run/emqx-secrets

# 挂载 tmpfs(大小按需设置,1M 足够存放私钥)
mount -t tmpfs -o size=1M,mode=700,uid=emqx,gid=emqx tmpfs /run/emqx-secrets

# 持久化挂载(重启后自动生效)
echo 'tmpfs /run/emqx-secrets tmpfs size=1M,mode=700,uid=emqx,gid=emqx,noexec,nosuid,nodev 0 0' \
  >> /etc/fstab
步骤三:编写启动前拉取脚本

创建 /usr/local/bin/emqx-fetch-secrets.sh

复制代码
#!/bin/bash
set -euo pipefail

SECRET_NAME="emqx/tls/server-key"
OUTPUT_PATH="/run/emqx-secrets/key.pem"

# 从 Secrets Manager 获取并解密(KMS 自动解密)
aws secretsmanager get-secret-value \
  --secret-id "$SECRET_NAME" \
  --query 'SecretString' \
  --output text > "$OUTPUT_PATH"

# 严格权限
chown emqx:emqx "$OUTPUT_PATH"
chmod 400 "$OUTPUT_PATH"

echo "[INFO] EMQX TLS key fetched to $OUTPUT_PATH"

chmod +x /usr/local/bin/emqx-fetch-secrets.sh
步骤四:集成到 EMQX 启动流程

方式 A:Systemd Override(推荐)

复制代码
systemctl edit emqx

添加以下内容:

复制代码
[Service]
ExecStartPre=/usr/local/bin/emqx-fetch-secrets.sh
# 确保 tmpfs 已挂载
ExecStartPre=/bin/mountpoint -q /run/emqx-secrets || /bin/mount /run/emqx-secrets

然后重载并重启:

复制代码
systemctl daemon-reload
systemctl restart emqx

方式 B:Docker / ECS / EKS 环境

在容器 entrypoint 或 init container 中执行拉取脚本,确保在 EMQX 主进程启动前完成。

步骤五:修改 EMQX 配置指向内存文件

由于私钥现在通过文件路径提供,你需要在 emqx.conf 中配置(而非 Dashboard),因为 Dashboard 不支持引用外部文件路径来替代其内部存储:

复制代码
listeners.ssl.default {
  bind = "0.0.0.0:9993"
  ssl_options {
    certfile = "/etc/emqx/certs/cert.pem"
    keyfile  = "/run/emqx-secrets/key.pem"    # ← 指向内存文件系统
    cacertfile = "/etc/emqx/certs/cacert.pem"
    verify = verify_peer
    fail_if_no_peer_cert = true
  }
}

⚠️ 重要冲突提醒 :如果你之前已在 Dashboard 上为同一监听器配置了证书,Dashboard 的配置优先级高于 emqx.conf。你需要先在 Dashboard 中删除该监听器的 SSL 证书配置,或删除整个监听器后重新通过配置文件创建,否则 EMQX 仍会使用 Dashboard 中存储的旧证书数据而忽略文件路径。

步骤六:验证
复制代码
# 1. 确认内存文件存在且权限正确
ls -la /run/emqx-secrets/key.pem
# 期望输出: -r-------- 1 emqx emqx ... key.pem

# 2. 确认是 tmpfs
df -h /run/emqx-secrets
# 期望输出: tmpfs  1.0M  ... /run/emqx-secrets

# 3. 重启 EMQX 后测试 mTLS 连接
mosquitto_pub -h 127.0.0.1 -p 9993 \
  --cafile cacert.pem --cert client-cert.pem --key client-key.pem \
  -t "test/mtls" -m "hello" -d --insecure
安全加固清单
项目 措施
tmpfs 防交换 noexec,nosuid,nodev 已在 fstab 中设置
私钥生命周期 仅存在于内存,EMQX 停止后可选清理:rm -f /run/emqx-secrets/key.pem
AWS 权限最小化 EC2/ECS 角色仅授予 secretsmanager:GetSecretValue + kms:Decrypt 对应密钥
审计 开启 CloudTrail 监控 Secrets Manager 和 KMS 的访问记录
轮换 更新 Secrets Manager 中的值后,执行 systemctl reload emqx 触发重新拉取(需配合 SIGHUP 处理或重启)

使用:

bash 复制代码
curl -s https://packagecloud.io/install/repositories/emqx/emqx/script.deb.sh | sudo bash

sudo apt-get update
sudo apt-get install emqx


systemctl start emqx
systemctl enable emqx

emqx ctl status

emqx ctl config show

mosquitto_pub \
  -h 0.0.0.0 \
  -p 9993 \
  --cafile cacert.pem \
  --cert client-cert.pem \
  --key client-key.pem \
  -t "test/mtls" \
  -m "hello mtls" \
  -d --insecure 

mosquitto_sub \
  -h 0.0.0.0 \
  -p 9993 \
  --cafile cacert.pem \
  --cert client-cert.pem \
  --key client-key.pem \
  -t "test/mtls" \
  -d --insecure -v