You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask+Gunicorn+MTLS场景下,如何将客户端证书传递至Flask接口?

解决Gunicorn+Flask架构下获取客户端MTLS证书的问题

以下是几种无需Nginx的可行方案,直接基于Gunicorn和Flask实现客户端证书的获取:

方案1:直接读取WSGI环境变量

Gunicorn在处理MTLS请求时,会将客户端证书相关信息存入WSGI环境变量中,Flask可以直接通过request.environ获取这些信息。

示例Flask视图代码:

from flask import Flask, request

app = Flask(__name__)

@app.route('/validate-cert')
def validate_cert():
    # 获取PEM格式的客户端证书
    client_cert = request.environ.get('SSL_CLIENT_CERT')
    # 获取客户端证书的DN(区分名)
    client_dn = request.environ.get('SSL_CLIENT_S_DN')
    # 获取证书校验结果
    cert_verify = request.environ.get('SSL_CLIENT_VERIFY')

    if not client_cert or cert_verify != 'SUCCESS':
        return "Invalid or missing client certificate", 403

    # 这里可以添加证书的自定义校验逻辑,比如检查DN、有效期等
    return f"Authenticated client: {client_dn}", 200

注意:确保Gunicorn启动时配置了正确的MTLS参数,例如:

gunicorn --certfile=server.crt --keyfile=server.key --ca-certs=ca.crt --cert-reqs=REQUIRED myapp:app

其中--cert-reqs=REQUIRED会强制要求客户端提供合法证书,否则Gunicorn直接拒绝请求。

方案2:通过Gunicorn钩子传递证书到请求头

如果需要更灵活的处理,可以通过Gunicorn的on_request钩子将证书信息编码后添加到请求头,方便Flask读取。

创建Gunicorn配置文件gunicorn.conf.py:

import base64

def on_request(request, environ):
    client_cert = environ.get('SSL_CLIENT_CERT')
    if client_cert:
        # 对证书进行Base64编码,避免换行符导致请求头异常
        encoded_cert = base64.b64encode(client_cert.encode('utf-8')).decode('utf-8')
        environ['HTTP_X_CLIENT_CERT'] = encoded_cert
    return request, environ

启动Gunicorn时指定配置文件:

gunicorn --config gunicorn.conf.py --certfile=server.crt --keyfile=server.key --ca-certs=ca.crt --cert-reqs=REQUIRED myapp:app

Flask中读取证书:

import base64

@app.route('/validate-cert')
def validate_cert():
    encoded_cert = request.headers.get('X-Client-Cert')
    if not encoded_cert:
        return "Missing client certificate", 403
    
    # 解码Base64证书
    client_cert = base64.b64decode(encoded_cert).decode('utf-8')
    # 后续校验逻辑...
    return "Certificate received", 200

方案3:自定义Gunicorn Worker获取证书

如果默认Gunicorn Worker未将证书信息存入WSGI环境,可以自定义Worker类,直接从客户端连接中提取证书。

创建自定义Worker类(例如在myapp.py中):

from gunicorn.workers.sync import SyncWorker
import ssl

class MTLSWorker(SyncWorker):
    def handle_request(self, listener, req, client, addr):
        # 从客户端连接获取证书(DER格式)
        der_cert = client.getpeercert(True)
        if der_cert:
            # 转换为PEM格式
            pem_cert = ssl.DER_cert_to_PEM_cert(der_cert)
            # 存入WSGI环境
            req.environ['SSL_CLIENT_CERT'] = pem_cert
            # 提取证书DN信息
            cert_details = client.getpeercert()
            req.environ['SSL_CLIENT_S_DN'] = ', '.join([f"{k}={v}" for k, v in cert_details['subject'][0]])
        
        # 调用父类方法处理请求
        super().handle_request(listener, req, client, addr)

启动Gunicorn时指定自定义Worker:

gunicorn --worker-class=myapp.MTLSWorker --certfile=server.crt --keyfile=server.key --ca-certs=ca.crt --cert-reqs=REQUIRED myapp:app

Flask中读取证书的方式同方案1。

关键注意事项

  • 确认Gunicorn版本兼容性:部分旧版本Gunicorn可能对WSGI环境中的SSL变量命名不同,可通过打印request.environ查看具体字段。
  • 证书校验逻辑:获取证书后,建议在Flask中补充二次校验(例如检查证书有效期、所属CA等),增强安全性。
  • 性能考量:自定义Worker或钩子会增加请求处理的少量开销,高并发场景建议测试性能影响。

内容的提问来源于stack exchange,提问作者juan carlos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 07:43:23