#!/usr/bin/env bash # #region check_llm_certs [C:4] [TYPE Module] [SEMANTICS ssl, certs, diagnostics, openssl, nss] # @BRIEF Diagnóstico SSL pro ss-tools. Testuje všechny vrstvy: OpenSSL, # Python httpx (jako LLMClient), Python requests (jako translate), # NSS Chromium (jako Playwright). Výstup použije AI k opravě kódu. # @USAGE ./scripts/check_llm_certs.sh [--target https://...] [-v] # @LAYER Infrastructure # @EXAMPLE: # ./scripts/check_llm_certs.sh --target https://lite.ai.rusal.com # #endregion check_llm_certs set -euo pipefail RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' PASS="${GREEN}✅${NC}"; FAIL="${RED}❌${NC}"; WARN="${YELLOW}⚠${NC}"; INFO="${CYAN}ℹ${NC}" TARGET_URL="${1:-https://lite.ai.rusal.com}" HOST="$(echo "$TARGET_URL" | sed 's|https://||;s|/.*$||')" CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt" CA_DIR="/etc/ssl/certs" NSS_DB="sql:${HOME:-/root}/.pki/nssdb" echo -e "${CYAN}╔════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ LLM SSL Certificate Diagnostic Report ║${NC}" echo -e "${CYAN}║ Target: ${TARGET_URL}${NC}" echo -e "${CYAN}╚════════════════════════════════════════════════════╝${NC}" echo "" # ────────────────────────────────────────────── # 1. SYSTEM CA STORE # ────────────────────────────────────────────── echo -e "${CYAN}── 1. System CA Store ──${NC}" echo "ca_bundle=${CA_BUNDLE}" echo "ca_dir=${CA_DIR}" echo "total_in_bundle=$(grep -c 'BEGIN CERTIFICATE' "$CA_BUNDLE" 2>/dev/null || echo 0)" echo "total_hash_links=$(find "$CA_DIR" -maxdepth 1 -type l -name '[0-9a-f]*.[0-9]' 2>/dev/null | wc -l)" echo "" echo "custom_certs=" for dir in /usr/local/share/ca-certificates/custom /usr/local/share/ca-certificates/llm; do [ -d "$dir" ] || continue for f in "$dir"/*.pem "$dir"/*.crt; do [ -f "$f" ] || continue subj="$(openssl x509 -in "$f" -noout -subject 2>/dev/null | head -1 || echo '?')" issuer="$(openssl x509 -in "$f" -noout -issuer 2>/dev/null | head -1 || echo '?')" fp="$(openssl x509 -in "$f" -noout -fingerprint -sha256 2>/dev/null | sed 's/.*=//' || echo '?')" in_bundle="no" grep -qF "$fp" "$CA_BUNDLE" 2>/dev/null && in_bundle="yes" echo " $(basename $f) | subject=${subj} | issuer=${issuer} | in_bundle=${in_bundle}" done done # ────────────────────────────────────────────── # 2. OPENSSL s_client - 3 варианта # ────────────────────────────────────────────── echo "" echo -e "${CYAN}── 2. OpenSSL s_client ──${NC}" for variant in "CAfile=${CA_BUNDLE}" "CApath=${CA_DIR}" "default"; do echo "variant=${variant}" if [ "$variant" = "default" ]; then out=$(openssl s_client -connect "${HOST}:443" -servername "$HOST" < /dev/null 2>&1) else key=$(echo "$variant" | cut -d= -f1) val=$(echo "$variant" | cut -d= -f2-) out=$(openssl s_client -connect "${HOST}:443" -"$key" "$val" -servername "$HOST" < /dev/null 2>&1) fi code=$(echo "$out" | grep "Verify return code" | head -1) echo " result=${code}" done # ────────────────────────────────────────────── # 3. Python - httpx (LLMClient style) # ────────────────────────────────────────────── echo "" echo -e "${CYAN}── 3. Python httpx (как LLMClient) ──${NC}" python3 -u -c ' import os, sys, ssl, traceback target = os.environ.get("TARGET_URL", "https://lite.ai.rusal.com") ca_bundle = "/etc/ssl/certs/ca-certificates.crt" ca_dir = "/etc/ssl/certs" results = {} # A) SSLContext s cafile try: ctx = ssl.create_default_context(cafile=ca_bundle) import httpx r = httpx.get(target, verify=ctx, timeout=10) results["httpx+cafile"] = f"OK status={r.status_code}" except Exception as e: results["httpx+cafile"] = f"FAIL {type(e).__name__}: {e}" # B) SSLContext s cafile + capath try: ctx2 = ssl.create_default_context() ctx2.load_verify_locations(cafile=ca_bundle) if os.path.isdir(ca_dir): ctx2.load_verify_locations(capath=ca_dir) import httpx r = httpx.get(target, verify=ctx2, timeout=10) results["httpx+cafile+capath"] = f"OK status={r.status_code}" except Exception as e: results["httpx+cafile+capath"] = f"FAIL {type(e).__name__}: {e}" # C) requests s verify=cafile try: import requests r = requests.get(target, verify=ca_bundle, timeout=10) results["requests+cafile"] = f"OK status={r.status_code}" except Exception as e: results["requests+cafile"] = f"FAIL {type(e).__name__}: {e}" # D) requests s verify=capath try: import requests r = requests.get(target, verify=ca_dir, timeout=10) results["requests+capath"] = f"OK status={r.status_code}" except Exception as e: results["requests+capath"] = f"FAIL {type(e).__name__}: {e}" for k, v in results.items(): print(f" {k}={v}") ' 2>&1 # ────────────────────────────────────────────── # 4. NSS / Chromium # ────────────────────────────────────────────── echo "" echo -e "${CYAN}── 4. NSS Chromium ──${NC}" if command -v certutil &>/dev/null && certutil -L -d "$NSS_DB" >/dev/null 2>&1; then echo "nss_db=${NSS_DB}" echo "nss_certs_count=$(certutil -L -d "$NSS_DB" 2>/dev/null | wc -l)" echo "nss_custom_certs=" certutil -L -d "$NSS_DB" 2>/dev/null | grep -iE "rusal|llm-|custom-" | while read -r line; do echo " $line" done else echo "nss_db=unavailable" fi # ────────────────────────────────────────────── # 5. LLM_SSL_VERIFY env # ────────────────────────────────────────────── echo "" echo -e "${CYAN}── 5. LLM_SSL_VERIFY ──${NC}" raw="${LLM_SSL_VERIFY:-not_set}" echo "LLM_SSL_VERIFY=${raw}" # doporuceni echo "" echo -e "${CYAN}── 6. AI Recommendation Input ──${NC}" echo "To fix SSL issues, AI needs to know which methods work." echo "Copy the FULL output above and paste to the AI assistant."