Tests 4 variants: openssl s_client (CAfile, CApath, default), Python httpx (cafile, cafile+capath), requests (cafile, capath), NSS certutil. Output format is machine-parseable for AI diagnosis.
158 lines
6.8 KiB
Bash
Executable File
158 lines
6.8 KiB
Bash
Executable File
#!/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."
|