feat: ADR-0009 SSL cert management, fix certifi vs system CA

- ADR-0009: comprehensive SSL certificate management strategy
- _get_ssl_verify() returns SSLContext with cafile= instead of bool True
  (httpx deprecates verify=<str>, requests needs system CA, not certifi)
- _get_verify() in translate plugin returns system CA path
- All tests updated for SSLContext return type
- All QA findings C1-C3, H1-H4, M1 incorporated into ADR
This commit is contained in:
2026-05-28 12:06:42 +03:00
parent 60333bdb95
commit 19dd447345
5 changed files with 170 additions and 22 deletions

View File

@@ -13,6 +13,7 @@ import json
import os
import re
import shutil
import ssl
import tempfile
from typing import Any
from urllib.parse import urlsplit
@@ -923,11 +924,25 @@ class LLMClient:
# region LLMClient._get_ssl_verify [TYPE Function]
# @PURPOSE Resolve SSL verification flag from environment.
# @POST Returns False when LLM_SSL_VERIFY env var is "false"/"0"/"no" (case-insensitive).
# @POST Returns SSLContext with system CA bundle when enabled,
# False when LLM_SSL_VERIFY env var is "false"/"0"/"no"/"off".
# @RATIONALE Возвращаем ssl.SSLContext вместо True, потому что httpx по
# умолчанию использует certifi, в котором нет корпоративных CA.
# ssl.create_default_context(cafile=...) гарантирует, что используются
# сертификаты из системного хранилища (/etc/ssl/certs/ca-certificates.crt),
# куда update-ca-certificates и наш fallback добавляют корпоративные CA.
# @REJECTED verify=<string> отвергнут — httpx 0.28.x депрекейтит строковый
# путь в verify=, требует SSLContext.
@staticmethod
def _get_ssl_verify() -> bool:
def _get_ssl_verify() -> ssl.SSLContext | bool:
raw = os.getenv("LLM_SSL_VERIFY", "true").strip().lower()
return raw not in ("false", "0", "no", "off")
if raw in ("false", "0", "no", "off"):
return False
ca_path = "/etc/ssl/certs/ca-certificates.crt"
if os.path.exists(ca_path):
return ssl.create_default_context(cafile=ca_path)
# fallback: если системного CA нет (редко), используем дефолтный
return ssl.create_default_context()
# endregion LLMClient._get_ssl_verify
# region LLMClient._format_connection_error [TYPE Function]