fix: check_llm_certs.py — handle DER files, openssl timeout 20s, binary PEM reads
This commit is contained in:
@@ -60,8 +60,8 @@ def _lib_ver(name: str) -> str:
|
|||||||
|
|
||||||
def _count_certs(path: str) -> int:
|
def _count_certs(path: str) -> int:
|
||||||
try:
|
try:
|
||||||
with open(path) as f:
|
with open(path, "rb") as f:
|
||||||
return f.read().count("-----BEGIN CERTIFICATE-----")
|
return f.read().count(b"-----BEGIN CERTIFICATE-----")
|
||||||
except Exception:
|
except Exception:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -69,31 +69,31 @@ def _count_certs(path: str) -> int:
|
|||||||
def _cert_info(path: Path) -> dict:
|
def _cert_info(path: Path) -> dict:
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
info = {"file": str(path), "format": path.suffix}
|
info = {"file": str(path), "format": path.suffix}
|
||||||
r = sp.run(
|
|
||||||
["openssl", "x509", "-in", str(path), "-noout",
|
def _try_read(fmt: str) -> dict | None:
|
||||||
"-subject", "-issuer", "-fingerprint", "-sha256"],
|
args = ["openssl", "x509", "-in", str(path), "-noout",
|
||||||
capture_output=True, text=True, timeout=5,
|
"-subject", "-issuer", "-fingerprint", "-sha256"]
|
||||||
)
|
if fmt == "DER":
|
||||||
if r.returncode == 0:
|
args += ["-inform", "DER"]
|
||||||
|
r = sp.run(args, capture_output=True, text=True, timeout=5)
|
||||||
|
if r.returncode != 0:
|
||||||
|
return None
|
||||||
|
res = {}
|
||||||
for line in r.stdout.strip().split("\n"):
|
for line in r.stdout.strip().split("\n"):
|
||||||
if line.startswith("subject="): info["subject"] = line[8:]
|
if line.startswith("subject="): res["subject"] = line[8:]
|
||||||
elif line.startswith("issuer="): info["issuer"] = line[7:]
|
elif line.startswith("issuer="): res["issuer"] = line[7:]
|
||||||
elif line.startswith("SHA256 Fingerprint="): info["fingerprint"] = line[19:]
|
elif line.startswith("SHA256 Fingerprint="): res["fingerprint"] = line[19:]
|
||||||
else:
|
return res
|
||||||
r2 = sp.run(["openssl", "x509", "-in", str(path), "-inform", "DER", "-noout", "-subject"],
|
|
||||||
capture_output=True, text=True, timeout=5)
|
d = _try_read("PEM") or _try_read("DER") or {}
|
||||||
if r2.returncode == 0:
|
info["subject"] = d.get("subject", "UNKNOWN_FORMAT")
|
||||||
info["subject"] = r2.stdout.strip().replace("subject=", "")
|
info["issuer"] = d.get("issuer", "?")
|
||||||
info["format"] += " (DER)"
|
info["fingerprint"] = d.get("fingerprint", "?")
|
||||||
else:
|
info["format"] += " (DER)" if d is not None and _try_read("PEM") is None else ""
|
||||||
info["subject"] = "UNKNOWN_FORMAT"
|
fp = info["fingerprint"]
|
||||||
info.setdefault("subject", "?")
|
|
||||||
info.setdefault("issuer", "?")
|
|
||||||
info.setdefault("fingerprint", "?")
|
|
||||||
fp = info.get("fingerprint", "")
|
|
||||||
if fp and fp != "?" and CA_BUNDLE.exists():
|
if fp and fp != "?" and CA_BUNDLE.exists():
|
||||||
with open(CA_BUNDLE) as f:
|
with open(CA_BUNDLE, "rb") as f:
|
||||||
info["in_bundle"] = "yes" if fp in f.read() else "no"
|
info["in_bundle"] = "yes" if fp.encode() in f.read() else "no"
|
||||||
else:
|
else:
|
||||||
info["in_bundle"] = "unknown"
|
info["in_bundle"] = "unknown"
|
||||||
return info
|
return info
|
||||||
@@ -118,7 +118,7 @@ def _run(cmd: list[str], timeout: int = 15) -> tuple[int, str, str]:
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return -1, "", "command not found"
|
return -1, "", "command not found"
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
return -2, "", "timeout"
|
return -2, "", f"timeout ({timeout}s)"
|
||||||
|
|
||||||
|
|
||||||
def _make_ctx_cafile():
|
def _make_ctx_cafile():
|
||||||
@@ -184,7 +184,7 @@ def _diagnose(s: dict) -> str:
|
|||||||
v = str(s.get(k, ""))
|
v = str(s.get(k, ""))
|
||||||
issues.append(f"{k}={'OK' if '0 (ok)' in v else 'FAIL'}")
|
issues.append(f"{k}={'OK' if '0 (ok)' in v else 'FAIL'}")
|
||||||
for lib in ("httpx", "requests"):
|
for lib in ("httpx", "requests"):
|
||||||
for method in ("true", "cafile", "capath", "false"):
|
for method in ("true", "cafile", "cafile_capath", "capath", "false"):
|
||||||
v = str(s.get(f"{lib}_{method}", ""))
|
v = str(s.get(f"{lib}_{method}", ""))
|
||||||
issues.append(f"{lib}_{method}={'OK' if v.startswith('OK') else 'FAIL'}")
|
issues.append(f"{lib}_{method}={'OK' if v.startswith('OK') else 'FAIL'}")
|
||||||
# Recommendation
|
# Recommendation
|
||||||
@@ -250,33 +250,43 @@ def main() -> None:
|
|||||||
results["custom_certs_count"] = len(custom_certs)
|
results["custom_certs_count"] = len(custom_certs)
|
||||||
|
|
||||||
_section("3. OpenSSL s_client")
|
_section("3. OpenSSL s_client")
|
||||||
|
# Note: if behind a corporate proxy, openssl s_client may hang.
|
||||||
|
# Adding -verify_return_error and -msg for better diagnostics.
|
||||||
|
base_cmd = ["openssl", "s_client", "-connect", f"{host}:443",
|
||||||
|
"-servername", host, "-verify_return_error"]
|
||||||
for label, args in [
|
for label, args in [
|
||||||
("openssl_default", []),
|
("openssl_default", []),
|
||||||
("openssl_cafile", ["-CAfile", str(CA_BUNDLE)]),
|
("openssl_cafile", ["-CAfile", str(CA_BUNDLE)]),
|
||||||
("openssl_capath", ["-CApath", str(CA_DIR)]),
|
("openssl_capath", ["-CApath", str(CA_DIR)]),
|
||||||
]:
|
]:
|
||||||
cmd = ["openssl", "s_client", "-connect", f"{host}:443", "-servername", host] + args
|
cmd = base_cmd + args
|
||||||
rc, out, err = _run(cmd, 15)
|
rc, out, err = _run(cmd, 20)
|
||||||
_parse_openssl(label, rc, out, err)
|
_parse_openssl(label, rc, out, err)
|
||||||
|
|
||||||
# Chain test: combine all custom certs into one bundle
|
# Chain test: combine PEM certs into one bundle (skip DER files)
|
||||||
cert_files = []
|
cert_files = []
|
||||||
for d in CUSTOM_DIRS:
|
for d in CUSTOM_DIRS:
|
||||||
if d.is_dir():
|
if d.is_dir():
|
||||||
for f in d.iterdir():
|
for f in d.iterdir():
|
||||||
if f.suffix in (".pem", ".crt"):
|
if f.suffix in (".pem", ".crt") and f.is_file():
|
||||||
cert_files.append(f)
|
# Check if PEM
|
||||||
|
rc, _, _ = _run(["openssl", "x509", "-in", str(f), "-noout"], 3)
|
||||||
|
if rc == 0:
|
||||||
|
cert_files.append(f)
|
||||||
if len(cert_files) >= 3:
|
if len(cert_files) >= 3:
|
||||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".pem", delete=False) as tf:
|
with tempfile.NamedTemporaryFile(mode="wb", suffix=".pem", delete=False) as tf:
|
||||||
chain_path = tf.name
|
chain_path = tf.name
|
||||||
for name in ["RUSAL_ROOT", "Policy_CA", "RGM_Issuing"]:
|
for name in ["RUSAL_ROOT", "Policy_CA", "RGM_Issuing"]:
|
||||||
for f in cert_files:
|
for f in cert_files:
|
||||||
if name in f.name:
|
if name in f.name:
|
||||||
tf.write(f.read_text())
|
tf.write(f.read_bytes())
|
||||||
break
|
break
|
||||||
rc, out, err = _run(["openssl", "s_client", "-connect", f"{host}:443", "-CAfile", chain_path, "-servername", host], 15)
|
rc, out, err = _run(["openssl", "s_client", "-connect", f"{host}:443",
|
||||||
|
"-CAfile", chain_path, "-servername", host], 20)
|
||||||
os.unlink(chain_path)
|
os.unlink(chain_path)
|
||||||
_parse_openssl("openssl_chain", rc, out, err)
|
_parse_openssl("openssl_chain", rc, out, err)
|
||||||
|
else:
|
||||||
|
print(" openssl_chain: ⚠ < 3 PEM certs found, skipping")
|
||||||
|
|
||||||
_section("4. Python httpx (как LLMClient)")
|
_section("4. Python httpx (как LLMClient)")
|
||||||
_test_httpx("httpx_true", verify=True)
|
_test_httpx("httpx_true", verify=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user