跳转到内容
搜索文档

双向 TLS

最后更新 查看 MarkdownAgent 设置

双向 TLS(mTLS)身份验证要求客户端和服务器在 TLS 握手期间都出示证书。在 Cloudflare Access 实现中,您上传的 CA 用于验证客户端证书(服务器证书验证由标准 TLS 处理)。Access mTLS 有两个目的:

  • 对不使用身份提供商的设备进行身份验证 — 自动化系统和 IoT 设备可以通过出示客户端证书而不是通过 IdP 登录来证明其身份。
  • 添加第二身份验证因子 — 还可以要求通过 IdP 登录的团队成员出示有效的客户端证书,从而提供额外的安全层。

当您将根证书颁发机构(CA)上传到 Access 时,仅允许来自具有匹配客户端证书的设备的请求通过。当请求到达应用程序时,Access 会要求客户端出示证书。如果客户端无法出示有效的证书,该请求将被阻止。如果客户端出示了有效的证书,Access 会完成密钥交换以进行验证。

mTLS 握手图

强制执行 mTLS 身份验证

前提条件

  • 针对您要使用 mTLS 保护的主机名的 Access 应用程序
  • 为您的设备签发客户端证书的 CA。
    • CA 证书可以来自公开受信任的 CA 或自签名。

    • 在证书 Basic Constraints 中,CA 属性必须设置为 TRUE

    • 证书必须使用以下列出的签名算法之一:

      允许的签名算法

      x509.SHA1WithRSA

      x509.SHA256WithRSA

      x509.SHA384WithRSA

      x509.SHA512WithRSA

      x509.ECDSAWithSHA1

      x509.ECDSAWithSHA256

      x509.ECDSAWithSHA384

      x509.ECDSAWithSHA512

将 mTLS 添加到您的 Access 应用程序

  1. Cloudflare 仪表板中,前往 Zero Trust > Access controls(访问控制) > Service credentials(服务凭据) > Mutual TLS(双向 TLS)

  2. 选择 Add mTLS Certificate(添加 mTLS 证书)

  3. 为根 CA 输入任意名称。

  4. Certificate content 中,粘贴您的根 CA 内容。

    如果客户端证书由根 CA 直接签名,您只需上传根证书。如果客户端证书由中间证书签名,则必须上传完整的 CA 链(中间证书和根证书)。例如:

    -----BEGIN CERTIFICATE-----
    <intermediate.pem>
    -----END CERTIFICATE-----
    -----BEGIN CERTIFICATE-----
    <rootCA.pem>
    -----END CERTIFICATE-----
    请勿包含任何 SSL/TLS 服务器证书;Access 仅使用 CA 链来验证用户设备与 Cloudflare 之间的连接。
  1. 在 **Associated hostnames(关联的主机名)**中,输入将使用此证书的完全限定域名(FQDN)。

    这些 FQDN 将是 Access 策略中受保护资源所使用的主机名。您必须将根 CA 与受保护应用程序所使用的 FQDN 关联。

  2. 保存策略。

  3. 转到 Access controls(访问控制) > Policies(策略)

  4. 使用以下选择器之一创建 Access 策略

    • Valid Certificate(有效证书):允许任何能够通过根 CA 进行身份验证的客户端证书继续访问。
    • Common Name(通用名称):仅允许具有特定公用名称(Common Name)的客户端证书继续访问。
  5. 如果这是针对不需要通过 IdP 登录的客户端,请将策略 Action(操作) 设置为 Service Auth

    示例 mTLS 策略

    操作 规则类型 选择器
    Service Auth(服务身份验证) Include(包含) Common Name(通用名称) John Doe
  6. 保存策略,然后转到 Access controls(访问控制) > Applications(应用程序)

  7. 选择您想要强制执行 mTLS 的应用程序,然后选择 Configure(配置)。该应用程序必须包含在步骤 5 的 Associated hostnames(关联主机名) 列表中。

  8. Policies(策略) 选项卡中,添加您的 mTLS policy。

  9. 保存应用程序。

现在,您可以使用客户端证书对该应用程序进行身份验证。有关如何出示客户端证书的说明,请参阅测试 mTLS

测试 mTLS

使用 cURL 进行测试

要测试受 mTLS 策略保护的应用程序:

  1. 首先,尝试在没有客户端证书的情况下对该站点执行 curl。 此 curl 命令示例针对为 https://auth.example.com 设置了 Access 应用程序和策略的站点 example.com

    curl -sv https://auth.example.com

    如果请求中没有客户端证书,将显示 403 forbidden 响应且无法访问该站点。

  2. 现在,将您的客户端证书和密钥添加到请求中:

    curl -sv https://auth.example.com --cert example.pem --key key.pem

当身份验证过程成功完成时,响应中会返回 CF_Authorization Set-Cookie 标头。

在浏览器中测试

要在浏览器中访问受 mTLS 保护的应用程序,必须将客户端证书导入到浏览器的证书管理器中。具体说明因浏览器而异。您的浏览器可能使用操作系统的根存储库或其自身的内部信任存储库。

以下示例演示了如何将客户端证书添加到 macOS 系统钥匙串中:

  1. 导航到包含客户端证书和密钥的目录。
    1. 在 Keychain Access 中打开 client.pem 文件。如果提示,请输入您的本地密码。
    2. Keychain(钥匙串) 中,选择适合您需求的访问选项,然后选择 Add(添加)
    3. 在证书列表中,找到新安装的证书。Keychain Access 会将此证书标记为不受信任。右键单击该证书并选择 Get Info(获取信息)
    4. 选择 Trust(信任)。在 When using this certificate(使用此证书时) 下,选择 Always Trust(始终信任)

假设您的浏览器使用 macOS 系统存储库,您现在可以通过浏览器连接到 mTLS 应用程序。

生成 mTLS 证书

您可以使用开源的私钥基础设施(PKI)工具生成证书,以测试 Cloudflare Access 中的 mTLS 功能。

OpenSSL

本节介绍如何使用 OpenSSL 生成根证书和中间证书,然后签发可针对 CA 链进行身份验证的客户端证书。

生成根 CA

  1. 生成根 CA 私钥:

     openssl genrsa -aes256 -out rootCA.key 4096

    系统提示时,输入与 rootCA.key 配合使用的密码。

  2. 创建一个名为 rootCA.pem 的自签名根证书:

    openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.pem

    系统将提示您输入私钥密码并填写一些可选字段。为了测试目的,您可以将可选字段留空。

生成中间证书

  1. 生成中间 CA 私钥:

     openssl genrsa -aes256 -out intermediate.key 4096

    系统提示时,输入与 intermediate.key 配合使用的密码。

  2. 为中间证书创建证书签名请求(CSR):

    openssl req -new -sha256 -key intermediate.key -out intermediate.csr

    系统将提示您输入私钥密码并填写一些可选字段。为了测试目的,您可以将可选字段留空。

  3. 创建一个名为 v3_intermediate_ca.ext 的 CA 扩展文件。例如:

    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always,issuer
    basicConstraints = critical, CA:true
    keyUsage = critical, cRLSign, keyCertSign

    确保 basicConstraints 包含 CA:true 属性。该属性允许中间证书充当 CA 并对客户端证书进行签名。

  4. 使用根 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

创建 CA 链文件

  1. 将中间证书和根证书合并为单个文件:

    cat intermediate.pem rootCA.pem > ca-chain.pem

    中间证书应位于文件的顶部,其后是其签名证书。

  2. ca-chain.pem 的内容上传到 Cloudflare Access。有关说明,请参阅将 mTLS 添加到您的 Access 应用程序

生成客户端证书

  1. 为客户端生成私钥:

     openssl genrsa -out client.key 2048
  2. 为客户端证书创建 CSR:

    openssl req -new -key client.key -out client.csr

    系统将提示您填写一些可选字段。为了测试目的,您可以将 Common Name(通用名称) 设置为类似于 John Doe 的名称。

  3. 使用中间证书对客户端证书进行签名:

     openssl x509 -req -in client.csr -CA intermediate.pem -CAkey intermediate.key -CAcreateserial -out client.pem -days 365 -sha256
  4. 验证客户端证书是否符合证书链:

    openssl verify -CAfile ca-chain.pem client.pem
    client.pem: OK

您现在可以使用客户端证书(client.pem)及其密钥(client.key)来测试 mTLS

Cloudflare PKI

本指南使用 Cloudflare 的 PKI 工具包从 JSON 文件生成根 CA 和客户端证书。

1. 安装依赖项

该过程需要 Cloudflare 的 PKI 工具包中的两个包:

  • cf-ssl
  • cfssljson

您可以从 Cloudflare SSL GitHub 仓库安装这些包。您需要安装并正常运行 Go 1.12 或更高版本。或者,您可以直接下载这些包。 使用“安装(Installation)”下的说明来安装该工具包,并确保您安装了该工具包中的所有实用程序。

2. 生成根 CA

  1. 创建一个新目录来存储根 CA。

  2. 在该目录中,创建两个新文件:

    • 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"
      			}
      		}
      	}
      }
  3. 现在,运行以下命令以使用这些文件生成根 CA。

    cfssl gencert -initca ca-csr.json | cfssljson -bare ca
  4. 该命令将输出根证书(ca.pem)及其密钥(ca-key.pem)。

    ls
    ca-config.json ca-csr.json ca-key.pem ca.csr  ca.pem
  5. ca.pem 的内容上传到 Cloudflare Access。有关说明,请参阅将 mTLS 添加到您的 Access 应用程序

3. 生成客户端证书

要生成可针对上传的根 CA 进行身份验证的客户端证书:

  1. 创建一个名为 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"
    		}
    	]
    }
  2. 现在,使用以下命令通过 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)。该列表将包含已被吊销的客户端证书。

  1. 从之前生成的客户端证书中获取序列号。在文本文件中以十六进制格式添加该序列号,或您打算吊销的任何其他序列号。此示例使用名为 serials.txt 的文件。

  2. 使用以下命令创建 CRL。

    cfssl gencrl serials.txt ../mtls-test/ca.pem ../mtls-test/ca-key.pem | base64 -D > ca.crl

您需要将 CRL 添加到您的服务器中,或者在 Cloudflare Worker 中强制执行此吊销。可以在 Cloudflare GitHub 仓库上找到示例 Worker 脚本。

添加 Client-Cert 和 Client-Cert-Chain 标头(RFC 9440)

RFC 9440 定义了 Client-CertClient-Cert-Chain HTTP 标头字段,用于将客户端证书信息传递给源站服务器。你可以使用请求标头修改规则和以下 Ruleset Engine 字段构造这些标头:

如字段定义所示,这些字段可设为空字符串或有效的 RFC 9440 编码。正确用法取决于以下各节讨论的几个因素。

安全注意事项

无论证书验证结果如何,cert_rfc9440cert_chain_rfc9440 字段都会被填充。这意味着客户端可以出示无效、过期或自签名证书,字段仍会包含编码的证书数据。在信任这些值之前,务必检查以下字段:

客户端还可能在请求中包含自己的 Client-CertClient-Cert-Chain 标头以注入任意值。如 RFC 9440 安全注意事项 所述,你必须无条件移除入站请求中任何现有的 Client-CertClient-Cert-Chain 标头,无论证书是否有效。这可防止客户端注入源站会信任的伪造证书数据。

有关如何配置 mTLS 和证书验证的详情,请参阅启用 mTLS

大小限制

编码的叶证书限制为 10 KiB,编码的证书链限制为 16 KiB。若编码值超出限制,对应字段将包含空字符串。使用以下字段检查此情况:

Transform Rules 示例

以下示例说明如何安全使用这些字段构造可信的 Client-CertClient-Cert-Chain 标头并转发到源站。 源站随后可依赖这些标头的存在,确信客户端出示了有效证书。 注意:当客户端未提供任何中间证书(仅叶证书)时,可省略 Client-Cert-Chain 标头。

你需要创建以下请求标头修改规则。 Remove 规则必须放在 Set dynamic 规则之前, 以便在设置已验证的值之前,在每个请求上剥离客户端注入的标头。

规则 1 — 移除 Client-Cert 标头

此规则无条件移除客户端发送的任何 Client-Cert 标头。

Expression Editor(表达式编辑器) 中的文本:

true

Modify request header(修改请求标头) 下选择的操作:Remove

Header name(标头名称)Client-Cert

规则 2 — 移除 Client-Cert-Chain 标头

此规则无条件移除客户端发送的任何 Client-Cert-Chain 标头。

Expression Editor(表达式编辑器) 中的文本:

true

Modify request header(修改请求标头) 下选择的操作:Remove

Header name(标头名称)Client-Cert-Chain

规则 3 — 设置 Client-Cert 标头

此规则仅在客户端出示有效、未吊销且在大小限制内的证书时设置 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_large

Modify request header(修改请求标头) 下选择的操作:Set dynamic

Header name(标头名称)Client-Cert

Value(值)cf.tls_client_auth.cert_rfc9440

规则 4 — 设置 Client-Cert-Chain 标头

此规则仅在客户端出示有效、未吊销的证书, 且证书链非空并在大小限制内时设置 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_large

Modify request header(修改请求标头) 下选择的操作:Set dynamic

Header name(标头名称)Client-Cert-Chain

Value(值)cf.tls_client_auth.cert_chain_rfc9440

Cloudflare Workers

你也可以在 Cloudflare Worker 中使用入站请求上的 tlsClientAuth 属性构造 RFC 9440 标头。

上述相同的安全注意事项同样适用。

转发客户端证书(旧版)

除为主机强制执行 mTLS 认证外,您还可以将客户端证书作为 HTTP 标头转发到源站服务器。此设置通常有助于服务器日志记录。

为避免在每个请求中添加证书,证书仅在 mTLS 连接的第一个请求上转发。

Cloudflare API

转发证书的最常见方法是使用 Cloudflare API 更新 mTLS 证书的主机名设置

Required API token permissions

At least one of the following token permissions is required:
  • Access: Mutual TLS Certificates Write
Update an mTLS certificate's hostname settingsbash
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-Base64
  • Cf-Client-Cert-Sha256

Managed Transforms

您还可以使用 Managed Transforms 修改 HTTP 响应标头,以传递 TLS client auth headers

Cloudflare Workers

此外,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 目前不适用于:

双向 TLS 证书的通知

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 / filters

None.

Included with

Purchase of Access and/or Cloudflare for SaaS.

What should you do if you receive one?

Upload a renewed certificate.

这篇文档对您有帮助吗?