双向 TLS(mTLS)身份验证 ↗要求客户端和服务器在 TLS 握手期间都出示证书。在 Cloudflare Access 实现中,您上传的 CA 用于验证客户端证书(服务器证书验证由标准 TLS 处理)。Access mTLS 有两个目的:
- 对不使用身份提供商的设备进行身份验证 — 自动化系统和 IoT 设备可以通过出示客户端证书而不是通过 IdP 登录来证明其身份。
- 添加第二身份验证因子 — 还可以要求通过 IdP 登录的团队成员出示有效的客户端证书,从而提供额外的安全层。
当您将根证书颁发机构(CA)上传到 Access 时,仅允许来自具有匹配客户端证书的设备的请求通过。当请求到达应用程序时,Access 会要求客户端出示证书。如果客户端无法出示有效的证书,该请求将被阻止。如果客户端出示了有效的证书,Access 会完成密钥交换以进行验证。
- 针对您要使用 mTLS 保护的主机名的 Access 应用程序。
- 为您的设备签发客户端证书的 CA。
-
CA 证书可以来自公开受信任的 CA 或自签名。
-
在证书
Basic Constraints中,CA属性必须设置为TRUE。 -
证书必须使用以下列出的签名算法之一:
允许的签名算法
x509.SHA1WithRSAx509.SHA256WithRSAx509.SHA384WithRSAx509.SHA512WithRSAx509.ECDSAWithSHA1x509.ECDSAWithSHA256x509.ECDSAWithSHA384x509.ECDSAWithSHA512
-
-
在 Cloudflare 仪表板 ↗中,前往 Zero Trust > Access controls(访问控制) > Service credentials(服务凭据) > Mutual TLS(双向 TLS)。
-
选择 Add mTLS Certificate(添加 mTLS 证书)。
-
为根 CA 输入任意名称。
-
在 Certificate content 中,粘贴您的根 CA 内容。
如果客户端证书由根 CA 直接签名,您只需上传根证书。如果客户端证书由中间证书签名,则必须上传完整的 CA 链(中间证书和根证书)。例如:
请勿包含任何 SSL/TLS 服务器证书;Access 仅使用 CA 链来验证用户设备与 Cloudflare 之间的连接。-----BEGIN CERTIFICATE----- <intermediate.pem> -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- <rootCA.pem> -----END CERTIFICATE-----
-
在 **Associated hostnames(关联的主机名)**中,输入将使用此证书的完全限定域名(FQDN)。
这些 FQDN 将是 Access 策略中受保护资源所使用的主机名。您必须将根 CA 与受保护应用程序所使用的 FQDN 关联。
-
保存策略。
-
转到 Access controls(访问控制) > Policies(策略)。
-
使用以下选择器之一创建 Access 策略:
- Valid Certificate(有效证书):允许任何能够通过根 CA 进行身份验证的客户端证书继续访问。
- Common Name(通用名称):仅允许具有特定公用名称(Common Name)的客户端证书继续访问。
-
如果这是针对不需要通过 IdP 登录的客户端,请将策略 Action(操作) 设置为 Service Auth。
示例 mTLS 策略
操作 规则类型 选择器 值 Service Auth(服务身份验证) Include(包含) Common Name(通用名称) John Doe -
保存策略,然后转到 Access controls(访问控制) > Applications(应用程序)。
-
选择您想要强制执行 mTLS 的应用程序,然后选择 Configure(配置)。该应用程序必须包含在步骤 5 的 Associated hostnames(关联主机名) 列表中。
-
在 Policies(策略) 选项卡中,添加您的 mTLS policy。
-
保存应用程序。
现在,您可以使用客户端证书对该应用程序进行身份验证。有关如何出示客户端证书的说明,请参阅测试 mTLS。
要测试受 mTLS 策略保护的应用程序:
-
首先,尝试在没有客户端证书的情况下对该站点执行 curl。 此 curl 命令示例针对为
https://auth.example.com设置了 Access 应用程序和策略的站点example.com:curl -sv https://auth.example.com如果请求中没有客户端证书,将显示
403 forbidden响应且无法访问该站点。 -
现在,将您的客户端证书和密钥添加到请求中:
curl -sv https://auth.example.com --cert example.pem --key key.pem
当身份验证过程成功完成时,响应中会返回 CF_Authorization Set-Cookie 标头。
要在浏览器中访问受 mTLS 保护的应用程序,必须将客户端证书导入到浏览器的证书管理器中。具体说明因浏览器而异。您的浏览器可能使用操作系统的根存储库或其自身的内部信任存储库。
以下示例演示了如何将客户端证书添加到 macOS 系统钥匙串中:
- 导航到包含客户端证书和密钥的目录。
- 在 Keychain Access 中打开
client.pem文件。如果提示,请输入您的本地密码。 - 在 Keychain(钥匙串) 中,选择适合您需求的访问选项,然后选择 Add(添加)。
- 在证书列表中,找到新安装的证书。Keychain Access 会将此证书标记为不受信任。右键单击该证书并选择 Get Info(获取信息)。
- 选择 Trust(信任)。在 When using this certificate(使用此证书时) 下,选择 Always Trust(始终信任)。
- 在 Keychain Access 中打开
假设您的浏览器使用 macOS 系统存储库,您现在可以通过浏览器连接到 mTLS 应用程序。
您可以使用开源的私钥基础设施(PKI)工具生成证书,以测试 Cloudflare Access 中的 mTLS 功能。
本节介绍如何使用 OpenSSL ↗ 生成根证书和中间证书,然后签发可针对 CA 链进行身份验证的客户端证书。
-
生成根 CA 私钥:
openssl genrsa -aes256 -out rootCA.key 4096系统提示时,输入与
rootCA.key配合使用的密码。 -
创建一个名为
rootCA.pem的自签名根证书:openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.pem系统将提示您输入私钥密码并填写一些可选字段。为了测试目的,您可以将可选字段留空。
-
生成中间 CA 私钥:
openssl genrsa -aes256 -out intermediate.key 4096系统提示时,输入与
intermediate.key配合使用的密码。 -
为中间证书创建证书签名请求(CSR):
openssl req -new -sha256 -key intermediate.key -out intermediate.csr系统将提示您输入私钥密码并填写一些可选字段。为了测试目的,您可以将可选字段留空。
-
创建一个名为
v3_intermediate_ca.ext的 CA 扩展文件。例如:subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = critical, CA:true keyUsage = critical, cRLSign, keyCertSign确保
basicConstraints包含CA:true属性。该属性允许中间证书充当 CA 并对客户端证书进行签名。 -
使用根 CA 对中间证书进行签名:
openssl x509 -req -in intermediate.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out intermediate.pem -days 1825 -sha256 -extfile v3_intermediate_ca.ext
-
将中间证书和根证书合并为单个文件:
cat intermediate.pem rootCA.pem > ca-chain.pem中间证书应位于文件的顶部,其后是其签名证书。
-
将
ca-chain.pem的内容上传到 Cloudflare Access。有关说明,请参阅将 mTLS 添加到您的 Access 应用程序。
-
为客户端生成私钥:
openssl genrsa -out client.key 2048 -
为客户端证书创建 CSR:
openssl req -new -key client.key -out client.csr系统将提示您填写一些可选字段。为了测试目的,您可以将 Common Name(通用名称) 设置为类似于
John Doe的名称。 -
使用中间证书对客户端证书进行签名:
openssl x509 -req -in client.csr -CA intermediate.pem -CAkey intermediate.key -CAcreateserial -out client.pem -days 365 -sha256 -
验证客户端证书是否符合证书链:
openssl verify -CAfile ca-chain.pem client.pemclient.pem: OK
您现在可以使用客户端证书(client.pem)及其密钥(client.key)来测试 mTLS。
本指南使用 Cloudflare 的 PKI 工具包 ↗从 JSON 文件生成根 CA 和客户端证书。
该过程需要 Cloudflare 的 PKI 工具包中的两个包:
cf-sslcfssljson
您可以从 Cloudflare SSL GitHub 仓库 ↗安装这些包。您需要安装并正常运行 Go 1.12 或更高版本。或者,您可以直接下载这些包 ↗。 使用“安装(Installation)”下的说明来安装该工具包,并确保您安装了该工具包中的所有实用程序。
-
创建一个新目录来存储根 CA。
-
在该目录中,创建两个新文件:
-
CSR。创建一个名为
ca-csr.json的文件,添加以下 JSON 代码块,然后保存文件。{ "CN": "Access Testing CA", "key": { "algo": "rsa", "size": 4096 }, "names": [ { "C": "US", "L": "Austin", "O": "Access Testing", "OU": "TX", "ST": "Texas" } ] } -
config。创建一个名为
ca-config.json的文件,添加以下 JSON 代码块,然后保存文件。{ "signing": { "default": { "expiry": "8760h" }, "profiles": { "server": { "usages": ["signing", "key encipherment", "server auth"], "expiry": "8760h" }, "client": { "usages": ["signing", "key encipherment", "client auth"], "expiry": "8760h" } } } }
-
-
现在,运行以下命令以使用这些文件生成根 CA。
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
该命令将输出根证书(
ca.pem)及其密钥(ca-key.pem)。lsca-config.json ca-csr.json ca-key.pem ca.csr ca.pem -
将
ca.pem的内容上传到 Cloudflare Access。有关说明,请参阅将 mTLS 添加到您的 Access 应用程序。
要生成可针对上传的根 CA 进行身份验证的客户端证书:
-
创建一个名为
client-csr.json的文件,并添加以下 JSON 代码块:{ "CN": "James Royal", "hosts": [""], "key": { "algo": "rsa", "size": 4096 }, "names": [ { "C": "US", "L": "Austin", "O": "Access", "OU": "Access Admins", "ST": "Texas" } ] } -
现在,使用以下命令通过 Cloudflare PKI 工具包生成客户端证书:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client-csr.json | cfssljson -bare client
该命令将输出客户端证书文件(client.pem)及其密钥(client-key.pem)。您现在可以使用这些文件来测试 mTLS。
您也可以使用 Cloudflare PKI 工具包来生成证书吊销列表(CRL)。该列表将包含已被吊销的客户端证书。
-
从之前生成的客户端证书中获取序列号。在文本文件中以十六进制格式添加该序列号,或您打算吊销的任何其他序列号。此示例使用名为
serials.txt的文件。 -
使用以下命令创建 CRL。
cfssl gencrl serials.txt ../mtls-test/ca.pem ../mtls-test/ca-key.pem | base64 -D > ca.crl
您需要将 CRL 添加到您的服务器中,或者在 Cloudflare Worker 中强制执行此吊销。可以在 Cloudflare GitHub 仓库 ↗上找到示例 Worker 脚本。
RFC 9440 ↗ 定义了 Client-Cert 和 Client-Cert-Chain HTTP 标头字段,用于将客户端证书信息传递给源站服务器。你可以使用请求标头修改规则和以下 Ruleset Engine 字段构造这些标头:
cf.tls_client_auth.cert_rfc9440— 以 RFC 9440 格式编码的客户端叶证书(见参考)。cf.tls_client_auth.cert_chain_rfc9440— 以 RFC 9440 格式编码的证书链(不含叶证书)(见参考)。
如字段定义所示,这些字段可设为空字符串或有效的 RFC 9440 编码。正确用法取决于以下各节讨论的几个因素。
无论证书验证结果如何,cert_rfc9440 和 cert_chain_rfc9440 字段都会被填充。这意味着客户端可以出示无效、过期或自签名证书,字段仍会包含编码的证书数据。在信任这些值之前,务必检查以下字段:
cf.tls_client_auth.cert_verified— 客户端证书有效时返回true。cf.tls_client_auth.cert_revoked— 客户端证书已被吊销时返回true。
客户端还可能在请求中包含自己的 Client-Cert 或 Client-Cert-Chain 标头以注入任意值。如 RFC 9440 安全注意事项 ↗ 所述,你必须无条件移除入站请求中任何现有的 Client-Cert 和 Client-Cert-Chain 标头,无论证书是否有效。这可防止客户端注入源站会信任的伪造证书数据。
有关如何配置 mTLS 和证书验证的详情,请参阅启用 mTLS。
编码的叶证书限制为 10 KiB,编码的证书链限制为 16 KiB。若编码值超出限制,对应字段将包含空字符串。使用以下字段检查此情况:
cf.tls_client_auth.cert_rfc9440_too_large— 编码证书超过 10 KiB 时返回true。cf.tls_client_auth.cert_chain_rfc9440_too_large— 编码证书链超过 16 KiB 时返回true。
以下示例说明如何安全使用这些字段构造可信的 Client-Cert 和 Client-Cert-Chain 标头并转发到源站。
源站随后可依赖这些标头的存在,确信客户端出示了有效证书。
注意:当客户端未提供任何中间证书(仅叶证书)时,可省略 Client-Cert-Chain 标头。
你需要创建以下请求标头修改规则。 Remove 规则必须放在 Set dynamic 规则之前, 以便在设置已验证的值之前,在每个请求上剥离客户端注入的标头。
此规则无条件移除客户端发送的任何 Client-Cert 标头。
Expression Editor(表达式编辑器) 中的文本:
trueModify request header(修改请求标头) 下选择的操作:Remove
Header name(标头名称):Client-Cert
此规则无条件移除客户端发送的任何 Client-Cert-Chain 标头。
Expression Editor(表达式编辑器) 中的文本:
trueModify request header(修改请求标头) 下选择的操作:Remove
Header name(标头名称):Client-Cert-Chain
此规则仅在客户端出示有效、未吊销且在大小限制内的证书时设置 Client-Cert 标头。
Expression Editor(表达式编辑器) 中的文本:
cf.tls_client_auth.cert_verified
and not cf.tls_client_auth.cert_revoked
and not cf.tls_client_auth.cert_rfc9440_too_largeModify request header(修改请求标头) 下选择的操作:Set dynamic
Header name(标头名称):Client-Cert
Value(值):cf.tls_client_auth.cert_rfc9440
此规则仅在客户端出示有效、未吊销的证书,
且证书链非空并在大小限制内时设置 Client-Cert-Chain 标头。
Expression Editor(表达式编辑器) 中的文本:
cf.tls_client_auth.cert_verified
and not cf.tls_client_auth.cert_revoked
and cf.tls_client_auth.cert_chain_rfc9440 ne ""
and not cf.tls_client_auth.cert_chain_rfc9440_too_largeModify request header(修改请求标头) 下选择的操作:Set dynamic
Header name(标头名称):Client-Cert-Chain
Value(值):cf.tls_client_auth.cert_chain_rfc9440
你也可以在 Cloudflare Worker 中使用入站请求上的 tlsClientAuth 属性构造 RFC 9440 标头。
上述相同的安全注意事项同样适用。
除为主机强制执行 mTLS 认证外,您还可以将客户端证书作为 HTTP 标头转发到源站服务器。此设置通常有助于服务器日志记录。
为避免在每个请求中添加证书,证书仅在 mTLS 连接的第一个请求上转发。
转发证书的最常见方法是使用 Cloudflare API 更新 mTLS 证书的主机名设置。
Required API token permissions
At least one of the following token permissions is required:Access: Mutual TLS Certificates Write
curl "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/access/certificates/settings" \
--request PUT \
--header "X-Auth-Email: $CLOUDFLARE_EMAIL" \
--header "X-Auth-Key: $CLOUDFLARE_API_KEY" \
--json '{
"settings": [
{
"hostname": "<HOSTNAME>",
"china_network": false,
"client_certificate_forwarding": true
}
]
}'将 client_certificate_forwarding 设置为 true 后,mTLS 连接内的每个请求现在将包含以下标头:
Cf-Client-Cert-Der-Base64Cf-Client-Cert-Sha256
您还可以使用 Managed Transforms 修改 HTTP 响应标头,以传递 TLS client auth headers。
此外,Workers 可以提供有关客户端证书的详细信息。
const tlsHeaders = {
"X-CERT-ISSUER-DN": request.cf.tlsClientAuth.certIssuerDN,
"X-CERT-SUBJECT-DN": request.cf.tlsClientAuth.certSubjectDN,
"X-CERT-ISSUER-DN-L": request.cf.tlsClientAuth.certIssuerDNLegacy,
"X-CERT-SUBJECT-DN-L": request.cf.tlsClientAuth.certSubjectDNLegacy,
"X-CERT-SERIAL": request.cf.tlsClientAuth.certSerial,
"X-CERT-FINGER": request.cf.tlsClientAuth.certFingerprintSHA1,
"X-CERT-VERIFY": request.cf.tlsClientAuth.certVerify,
"X-CERT-NOTBE": request.cf.tlsClientAuth.certNotBefore,
"X-CERT-NOTAF": request.cf.tlsClientAuth.certNotAfter,
};mTLS 目前不适用于:
Cloudflare 将在您的双向 TLS 证书过期之前发送以下通知:
Access mTLS Certificate Expiration Alert
Who is it for?Access customers that use client certificates for mutual TLS authentication. This notification will be sent 30 and 14 days before the expiration of the certificate.
Other options / filtersNone.
Included withPurchase of Access and/or Cloudflare for SaaS.
What should you do if you receive one?Upload a renewed certificate.