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

如何让Python在Docker容器中找到CA证书文件解决SSL验证失败问题

解决Docker容器间TLS请求的证书验证失败问题

问题背景

我部署了两个Docker容器app_1和app_2,app_1通过FastAPI向app_2发起REST请求,连接需通过TLS协议。我为每个应用创建了自签名证书,并使用自建CA进行签名,通过以下命令启动:

docker-compose --env-file my.env up --build

错误信息

发起请求时出现以下错误:

File "/usr/local/lib/python3.9/ssl.py", line 1310, in do_handshake
app_1 | self._sslobj.do_handshake() app_1 |
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: self signed certificate in certificate
chain (_ssl.c:1129)

我知道需要告知requests库CA证书包的位置,但不清楚具体操作方法。相关配置与代码如下:

环境文件内容

REQUESTS_CA_BUNDLE=/app/certs/chain.pem 

chain.pem包含CA.crt、CA.key、app_2.crt和app_2.key文件。

app_1代码

from fastapi import FastAPI
import requests
import time
import sched 


app = FastAPI()

ca_cert_file = '/app/certs/ca.crt'
ca_key_file = '/app/certs/ca.key'


@app.get('/')
async def read_root():
    return {"message": "Hello from app_1"}

    
def make_request():
    url = "https://app_2"

    response = requests.get(url, timeout=10, cert=('/app/certs/ca.crt', '/app/certs/ca.key'))

    if response.status_code == 200:
       print(response.json())
    else:
        print(f"Request failed status code: {response.status_code}") 


scheduler = sched.scheduler(time.time, time.sleep)

def make_rest_call_periodically():
    make_request()

    scheduler.enter(10, 10, make_rest_call_periodically)

scheduler.enter(1, 1, make_rest_call_periodically)

scheduler.run() 

if __name__ == "__main__":
    import uvicorn 
    uvicorn.run(app, host="0.0.0.0", port=443, ssl_ca_certs='/app/certs/ca.crt', ssl_keyfile="/app/certs/app_1.key", ssl_certfile="/app/certs/app_1.crt")

app_2代码

from fastapi import FastAPI
import ssl
import uvicorn 
import requests
import time
import urllib3

app = FastAPI()

@app.get('/')
async def read_root():
    return {"message": "Hello from app_2"}


if __name__ == "__main__":
    import uvicorn 

    uvicorn.run(app, host="0.0.0.0", port=443, ssl_ca_certs='/app/certs/ca.crt', ssl_keyfile="/app/certs/app_2.key", ssl_certfile="/app/certs/app_2.crt")

解决方案

问题根源

你在requests.get中错误使用了cert参数:这个参数是用来指定客户端证书(app_1向app_2证明自身身份的凭证),而非信任的CA证书。当前报错是因为requests默认不信任自建CA签名的app_2证书链,因此需要明确告知requests信任你的自建CA。

修复步骤

  1. 修正requests请求参数
    将make_request函数中的cert参数替换为verify,指定自建CA的证书路径:

    def make_request():
        url = "https://app_2"
        # 使用verify指定信任的CA证书,验证app_2的服务器证书
        response = requests.get(url, timeout=10, verify='/app/certs/ca.crt')
    
        if response.status_code == 200:
            print(response.json())
        else:
            print(f"Request failed status code: {response.status_code}")
    

    如果你想通过环境变量REQUESTS_CA_BUNDLE生效,只需确保chain.pem仅包含CA公钥证书(CA.crt),然后去掉verify参数,requests会自动读取该环境变量。

  2. 清理并修复chain.pem
    当前chain.pem混入了密钥文件(CA.key、app_2.key),这既不安全也不符合证书包规范。重新生成仅包含CA公钥的证书包:

    cat /app/certs/CA.crt > /app/certs/chain.pem
    

    密钥文件需单独存储,禁止放入证书包中。

  3. 调整app_2的客户端验证配置(可选)
    app_2启动时的ssl_ca_certs参数用于开启客户端证书验证(要求app_1提供身份证书):

    • 若不需要客户端验证,直接移除该参数;
    • 若需要客户端验证,app_1的请求需用cert参数指定自身的客户端证书(/app/certs/app_1.crt和/app/certs/app_1.key),同时保留verify参数指定CA证书,示例:
      response = requests.get(url, timeout=10, verify='/app/certs/ca.crt', cert=('/app/certs/app_1.crt', '/app/certs/app_1.key'))
      

验证修复效果

重启容器后,app_1的周期性请求应能正常建立TLS连接并获取app_2的响应。


内容的提问来源于stack exchange,提问作者Jim Archer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:33:07