Files
ss-tools/docker/certs.sh
busya 8fd23f7ea1 refactor(ssl): centralize SSL trust management, remove LLM_SSL_VERIFY
Centralized SSL via one contract: CERTS_PATH=/opt/certs mounted into all containers.

Backend:
  - NEW: backend/src/core/ssl.py — system_ssl_context(), httpx_verify(),
    cert_dir_inventory()
  - LLMClient._get_ssl_verify() → delegates to core.ssl
  - _llm_async_http._get_verify() → delegates to core.ssl
  - Removed LLM_SSL_VERIFY env reading from all runtime code

Docker:
  - NEW: docker/certs.sh — shared cert installer (PEM/DER/cer to .crt conversion,
    update-ca-certificates, hash symlinks, NSS import)
  - NEW: docker/agent.entrypoint.sh — agent entrypoint with cert installation
  - backend.entrypoint.sh → uses certs.sh instead of install_llm_ca_certs
  - Dockerfile.agent → adds ca-certificates, openssl, entrypoint

Compose:
  - Removed LLM_CA_CERT_URLS and LLM_SSL_VERIFY from all compose files
  - Added CERTS_PATH volume mount to agent (dev + enterprise)
  - Added certs volume mount to backend/agent in dev compose

Env examples:
  - Removed LLM_SSL_VERIFY, LLM_CA_CERT_URLS from .env.example,
    .env.enterprise-clean.example, .env.current.example, .env.master.example,
    backend/.env.example
  - Enhanced CERTS_PATH comments with accepted formats

Diagnostics:
  - diag_container.py: removed LLM_* checks, added CERTS_PATH inventory,
    uses core.ssl for context creation

Tests:
  - Updated test_llm_analysis_service, test_llm_async_http,
    test_client_headers to verify centralized ssl context (no env disable)
  - 4/4 SSL tests pass
2026-07-06 21:00:28 +03:00

159 lines
5.3 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# #region docker.certs [C:3] [TYPE Module] [SEMANTICS docker,certs,ssl,ca,trust]
# @BRIEF Shared certificate installation functions for all containers.
# @LAYER Infrastructure
# @PRE CERTS_PATH points to mounted /opt/certs directory (may be empty or absent).
# @POST Corporate CA certificates installed into system trust store and NSS DB.
# @INVARIANT server.crt, server.key, server.p12, *.key, *.p12, *.pfx are never imported as CA.
set -euo pipefail
# ── normalize_cert PEM/DER detection + conversion ──────────────────
normalize_cert() {
local input="$1"
local output="$2"
if openssl x509 -in "$input" -inform PEM -noout 2>/dev/null; then
cp "$input" "$output"
return 0
fi
if openssl x509 -in "$input" -inform DER -noout 2>/dev/null; then
openssl x509 -in "$input" -inform DER -out "$output" -outform PEM 2>/dev/null
return 0
fi
return 1
}
# ── install_all_certs single unified entry point ───────────────────
install_all_certs() {
local certs_dir="${CERTS_PATH:-/opt/certs}"
if [ ! -d "$certs_dir" ]; then
echo "[certs] CERTS_PATH=${certs_dir} не существует пропускаем"
return 0
fi
local target="/usr/local/share/ca-certificates/custom"
mkdir -p "$target"
echo "[certs] Устанавливаем корпоративные сертификаты из ${certs_dir}..."
local installed=0 skipped=0 invalid=0
for f in "$certs_dir"/*; do
[ -f "$f" ] || continue
local name
name="$(basename "$f")"
# Skip non-CA server/private files
case "$(echo "$name" | tr '[:upper:]' '[:lower:]')" in
server.crt|server.key|server.pem|*.key|*.p12|*.pfx)
echo "[certs] ⏭️ пропускаем: ${name} (server/private файл)"
skipped=$((skipped + 1))
continue
;;
esac
# Accept .crt, .pem, .cer, .der
case "$(echo "$name" | tr '[:upper:]' '[:lower:]')" in
*.crt|*.pem|*.cer|*.der)
# valid cert extension
;;
*)
echo "[certs] ⚠️ неизвестный формат: ${name} пропускаем"
skipped=$((skipped + 1))
continue
;;
esac
local out_name="${name%.*}.crt"
local out_file="${target}/${out_name}"
if normalize_cert "$f" "$out_file"; then
echo "[certs] ✅ ${name} (→ ${out_name})"
installed=$((installed + 1))
else
echo "[certs] ❌ невалидный сертификат: ${name}"
invalid=$((invalid + 1))
fi
done
if [ "$installed" -eq 0 ]; then
echo "[certs] Нет CA-сертификатов для установки"
return 0
fi
echo "[certs] Установлено: ${installed}, пропущено: ${skipped}, невалидно: ${invalid}"
# Update system CA store
if command -v update-ca-certificates &>/dev/null; then
echo "[certs] Обновляем системное хранилище CA-сертификатов..."
update-ca-certificates --fresh 2>&1 | sed 's/^/[certs] /'
fi
# Create hash symlinks for OpenSSL capath
echo "[certs] Создаём хеш-симлинки..."
local sym_count=0
for cert_file in "$target"/*.crt; do
[ -f "$cert_file" ] || continue
local cert_name
cert_name="$(basename "$cert_file" .crt)"
local hash_val
hash_val="$(openssl x509 -in "$cert_file" -noout -hash 2>/dev/null || true)"
if [ -n "$hash_val" ]; then
local suffix=0
local link_path="/etc/ssl/certs/${hash_val}.${suffix}"
while [ -L "$link_path" ]; do
if [ "$(readlink "$link_path")" = "$cert_file" ]; then
break 2
fi
suffix=$((suffix + 1))
link_path="/etc/ssl/certs/${hash_val}.${suffix}"
done
if [ ! -L "$link_path" ]; then
ln -sf "$cert_file" "$link_path"
sym_count=$((sym_count + 1))
fi
fi
done
echo "[certs] Создано хеш-симлинок: ${sym_count}"
}
# ── install_ca_to_nss NSS DB for Chromium/Playwright ──────────────
install_ca_to_nss() {
if ! command -v certutil &>/dev/null; then
return 0
fi
local target="/usr/local/share/ca-certificates/custom"
if [ ! -d "$target" ] || [ -z "$(ls -A "$target" 2>/dev/null)" ]; then
return 0
fi
local nssdb="${HOME}/.pki/nssdb"
mkdir -p "$nssdb"
local nss_count=0
echo "[certs] Импортируем сертификаты в NSS DB..."
for cert_file in "$target"/*.crt; do
[ -f "$cert_file" ] || continue
local cert_name
cert_name="$(basename "$cert_file" .crt)"
certutil -A -d "sql:${nssdb}" -t "C,," -n "custom-${cert_name}" -i "$cert_file" 2>/dev/null && nss_count=$((nss_count + 1)) || true
done
echo "[certs] Импортировано в NSS: ${nss_count}"
}
# ── skip_server_files remove server certs from CA dir ──────────────
skip_server_files_filter() {
# Used by frontend to skip server.crt from being installed as CA
local certs_dir="${1:-/opt/certs}"
[ -d "$certs_dir" ] || return 0
for f in "$certs_dir"/*.crt "$certs_dir"/*.pem; do
[ -f "$f" ] || continue
case "$(basename "$f" | tr '[:upper:]' '[:lower:]')" in
server.crt|server.key) continue ;;
esac
echo "$f"
done
}
# #endregion docker.certs