# 设备接入 mTLS 最佳实践

双向 TLS（mTLS）要求客户端和服务端相互验证证书，从根本上杜绝了未持有有效证书的设备接入。配合关闭端口密码认证，设备连接时无需提供用户名和密码，完全依赖 TLS 客户端证书完成身份验证。

完整配置分为以下三个步骤：

1. **签发客户端证书**：为每台设备签发独立的客户端证书，并在证书 CN 字段中写入设备唯一标识。
2. **在 EMQX Cloud 中配置 mTLS**：上传服务端证书和客户端 CA，开启双向认证，并将证书 CN 字段映射为 Username 或 ClientID。
3. **关闭端口密码认证**：关闭加密端口上的密码认证，设备凭证书即可完成接入。

## 证书字段映射

EMQX Cloud 支持将客户端证书中的特定字段自动映射为 MQTT 连接的 Username 或 ClientID。开启后，设备无需在连接时手动填写认证信息，EMQX 直接从证书中读取，后续的 ACL 权限规则也可基于该 Username 或 ClientID 进行配置。

可用于映射的字段包括 CN、DN、CRT 等，可在[部署设置](./deployment_settings.md#mqtt-设置)中按需选择。本文以最常用的 CN（Common Name）字段为例。使用 CN 字段时，建议填写设备序列号、MAC 地址或 UUID 作为设备唯一标识，且每台设备的 CN 必须全局唯一，不得与其他设备共享同一张证书。

## 准备证书

mTLS 需要以下两套证书：

- **服务端证书**：上传至 EMQX Cloud，用于客户端验证服务端身份。生成步骤请参考[生成服务端 CA 证书](./tls_ssl.md#generate-server-side-ca-certificate)。
- **客户端证书**：下发至每台设备，用于服务端验证设备身份。生成步骤见下文。

## 签发客户端证书

### 生成 Client CA

Client CA 用于为每台设备签发客户端证书，只需生成一次。请先确保已安装 [OpenSSL](https://www.openssl.org/)。

```bash
openssl req \
    -new \
    -newkey rsa:2048 \
    -days 3650 \
    -nodes \
    -x509 \
    -subj "/C=CN/O=EMQX Technologies Co., Ltd/CN=EMQ CA" \
    -keyout client-ca.key \
    -out client-ca.crt
```

生成的 `client-ca.crt` 需要在后续步骤中上传至 EMQX Cloud。

### 为每台设备签发证书

以下步骤为每台设备重复执行，将 `device-001` 替换为实际的设备标识。

1. 生成设备私钥。

   ```bash
   openssl genrsa -out device-001.key 2048
   ```

2. 生成证书请求文件，CN 填写设备唯一标识。

   ```bash
   openssl req -new -key device-001.key -out device-001.csr \
       -subj "/CN=device-001"
   ```

3. 用 Client CA 签名，生成设备证书。

   ```bash
   openssl x509 -req -days 3650 -sha256 \
       -in device-001.csr \
       -CA client-ca.crt \
       -CAkey client-ca.key \
       -CAcreateserial \
       -out device-001.crt
   ```

4. 验证证书。

   ```bash
   openssl verify -CAfile client-ca.crt device-001.crt
   ```

## 在 EMQX Cloud 中配置 mTLS

### 开启双向 TLS

在部署的 **TLS/SSL 配置**面板中上传服务端证书、私钥和客户端 CA 证书。详细的操作步骤请参考[在专有版中配置 TLS/SSL](./tls_ssl.md#configure-two-way-tls-ssl)。

### 配置 CN 映射 Username

在部署设置中将 CN 字段映射为 MQTT 连接的 `Username`，后续 ACL 规则可基于该 Username 进行配置。

::: tip
仅专有版 v5 部署支持。如果未看到相关配置选项，请[提交工单](../feature/tickets.md#工单)联系我们升级部署版本。
:::

1. 进入部署详情，点击左侧菜单中的**部署设置**，选择 **MQTT 设置** 标签页。
2. 在**使用对端证书作为用户名**下拉菜单中选择 `cn`。
3. 点击**保存**。

### 配置 CN 映射 ClientID

如果业务系统需要通过 ClientID 追踪设备，也可以将 CN 映射为 `ClientID`。

::: tip
仅专有版 v5 部署支持。如果未看到相关配置选项，请[提交工单](../feature/tickets.md#工单)联系我们升级部署版本。
:::

1. 进入部署详情，点击左侧菜单中的**部署设置**，选择 **MQTT 设置** 标签页。
2. 在**使用对端证书作为客户端 ID** 下拉菜单中选择 `cn`。
3. 点击**保存**。

::: warning 注意
使用 CN 映射 ClientID 时，必须确保每个设备的 CN 全局唯一。若多台设备共享相同的 CN，将因 ClientID 冲突导致连接相互踢出。
:::

## 关闭端口密码认证

完成以上配置后，可关闭加密端口上的密码认证，使设备仅凭客户端证书即可接入，无需提供用户名和密码。

::: warning 注意
关闭密码认证前，请确认所有客户端均已具备有效的客户端证书，并完成端到端的连通性测试，避免影响线上设备连接。同时建议关闭明文端口（1883），仅保留加密端口（8883 / 8084）对外提供服务。
:::

**专有版 v5 部署**可在控制台自助操作，步骤请参考[端口管理 - 管理加密端口的客户端认证](./port_management.md#管理加密端口的客户端认证)。

其他版本部署请提交支持[工单](../feature/tickets.md)，SRE 团队可以帮助您禁用部署中 8883 和 8084 端口上的认证。

## 客户端连接示例

### MQTTX 示例

使用 [MQTTX](https://mqttx.app/) 进行 mTLS 连接测试：

1. 新建连接，输入连接名称，Client ID 可随机生成。

2. 选择 Host，填入部署的连接地址和端口：
   - TLS 连接：选择 `mqtts://`，端口 `8883`。
   - WebSocket over TLS：选择 `wss://`，端口 `8084`。

3. 端口密码认证已关闭时，无需填写 Username 和 Password。

4. 启用 **SSL/TLS**，选择 **Self signed**。

5. 填写以下证书文件：
   - **CA File**：服务端 CA 证书（`server-ca.crt`）。
   - **Client Certificate File**：设备客户端证书（`device-001.crt`）。
   - **Client Key File**：设备客户端私钥（`device-001.key`）。

6. 点击右上角**连接**。

## 常见问题

### ClientID 冲突导致设备被踢下线

**现象：** 两台设备共享了相同的 ClientID 或同一张客户端证书，后连接的设备将先连接的设备踢下线。

**原因：** MQTT 协议规定同一 ClientID 在 Broker 中只能存在一个连接。

**解决方案：** 确保每台设备持有唯一 CN 的独立证书，避免共享证书。使用 CN 映射 Username 方案可以进一步规避 ClientID 冲突风险。

### 证书泄露后如何处置

1. 若已开启 CN 映射 Username，立即将泄露设备的 CN 对应的 Username 添加到[黑名单](./blacklist.md)，阻止其重新连接。
2. 向目标设备补发新证书，更新设备端证书配置。
3. 在黑名单条目到期或手动删除前，泄露的证书将无法用于连接。

### 关闭密码认证后是否还需要其他认证手段

关闭端口密码认证后，认证完全依赖 TLS 客户端证书，安全性高于用户名密码方案。如果部分设备暂时无法使用客户端证书，可在过渡期内保留密码认证，待完成全量证书化迁移后再关闭。
