此文章因原图片失效过多,以及cloudflare相关页面修改,于2023/05/11 重写。
CloudFlare在2020/10/1新推出了一个名为API Shield
的功能,配置详情与使用介绍见这里。此处说的就是客户端证书校验的功能(MTLS)。
大致过程为【生成证书=>选择开启校验的host=>建立防火墙规则=>客户端安装证书】。在上面提到的两篇文章也有具体使用介绍。不过还是记录下今日测试的使用过程。
生成证书# {#生成证书}
-
打开 域名->SSL/TLS->客户端证书
点击创建证书
-
创建客户端证书
创建 csr 直接选择cloudflare生成,或者 https://myssl.com/csr_create.html 在线生成,或者手动使用 Openssl 进行生成。此处如果自己生成的话,存留密钥。 -
保存客户端pem证书与密钥
保存pem格式的证书与给出的密钥(如果上一步选择cloudflare生成) -
开启mtls验证
此处也就是相当于nginx的ssl_verify_client optional; 当再次访问该域名时,服务端就会发送Certificate Request以让客户端选择证书(当客户端安装了对应的证书),但此时无论验证证书与否都可以正常访问。还需要定义一个防火墙规则来阻止没有通过证书验证的请求。
建立防火墙规则# {#建立防火墙规则}
在 /security/waf/custom-rules 进行添加规则
因cloudflare规则本身比较灵活,所以可以实现 为某域名下的某URL(比如后台管理)来开启证书校验等等。
主要生效的规则就是
not cf.tls_client_auth.cert_verified
客户端安装证书# {#客户端安装证书}
此时我们打开 刚才的域名 就会发现 Access denied
。因为此时防火墙规则已经生效,需要客户端安装对应证书才可以正常访问。例如Chrome支持的个人信息交换证书类型是 PKCS#12。所以此处需要拿到domain.key和domain.pem进行组合。
-
可以通过openssl进行转换:
过程中会让输入
export password
。根据个人喜好设定。如果设定的话,则在导入客户端时也需要输入该password
。
openssl pkcs12 -export -out xx.p12 -in xx.pem -inkey xx.key
-
通过在线工具转换
https://myssl.com/cert_convert.html
原格式: PEM 目标格式: PKCS12
完成之后就可以在客户端进行导入了,当安装客户端证书以后再访问相关的域名浏览器就会提示选择证书。选择刚导入的证书后即可正常访问。
此处有扫盲:
- .DER .CER,文件是二进制格式,只保存证书,不保存私钥。
- .PEM,一般是文本格式,可保存证书,可保存私钥。
- .CRT,可以是二进制格式,可以是文本格式,与 .DER 格式相同,不保存私钥。
- .PFX .P12,二进制格式,同时包含证书和私钥,一般有密码保护。
- .JKS,二进制格式,同时包含证书和私钥,一般有密码保护。
其他# {#其他}
此功能名为API Shield
。说明该功能主要为API设计,浏览器访问不属于考虑范畴,所以并未在创建时直接提供PKCS#12的类型。给出的默认案例是python:
import requests
import json
from datetime import datetime
def readSensor():
# Takes a reading from a temperature sensor and store it to temp_measurement
dateTimeObj = datetime.now()
timestampStr = dateTimeObj.strftime('%Y-%m-%dT%H:%M:%SZ')
measurement = {'temperature':str(36.5),'time':timestampStr}
return measurement
def main():
print("Cloudflare API Shield [IoT device demonstration]")
temperature = readSensor()
payload = json.dumps(temperature)
url = 'https://shield.upinatoms.com/temps'
json_headers = {'Content-Type': 'application/json'}
cert_file = ('/etc/ssl/certs/sensor.pem', '/etc/ssl/private/sensor-key.pem')
r = requests.post(url, headers = json_headers, data = payload, cert = cert_file)
print("Request body: ", r.request.body)
print("Response status code: %d" % r.status_code)