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