Add proxy support for SSL certificate fetching in ssl_check utility
This commit is contained in:
@@ -401,6 +401,8 @@ def ssl_check_cmd(url: str) -> str:
|
||||
|
||||
Falls back to a minimal Python-based peer certificate fetch if openssl is not available.
|
||||
"""
|
||||
from urllib.parse import urlparse
|
||||
|
||||
try:
|
||||
parsed = urlparse(url)
|
||||
host = parsed.hostname or url
|
||||
@@ -450,20 +452,74 @@ def ssl_check_cmd(url: str) -> str:
|
||||
logger.exception("OpenSSL check failed")
|
||||
return f"OpenSSL check failed: {e}\n"
|
||||
else:
|
||||
# Fallback: use Python ssl to fetch the peer certificate (leaf only)
|
||||
# Fallback: use Python ssl to fetch the peer certificate (leaf only).
|
||||
# If a proxy is configured, perform an HTTP CONNECT to the proxy first
|
||||
# and then do TLS over the established tunnel so the result matches the
|
||||
# path used by the running Python process.
|
||||
import ssl
|
||||
from urllib.parse import urlparse
|
||||
import base64
|
||||
|
||||
logger.debug("OpenSSL not found; using Python ssl fallback")
|
||||
def fetch_via_proxy(proxy_url: str) -> str:
|
||||
parsed = urlparse(proxy_url)
|
||||
proxy_host = parsed.hostname
|
||||
proxy_port = parsed.port or (443 if parsed.scheme == "https" else 80)
|
||||
|
||||
auth_header = None
|
||||
if parsed.username:
|
||||
user = parsed.username
|
||||
pwd = parsed.password or ""
|
||||
auth_header = base64.b64encode(f"{user}:{pwd}".encode("utf-8")).decode("ascii")
|
||||
|
||||
s = socket.create_connection((proxy_host, proxy_port), timeout=15)
|
||||
try:
|
||||
req = f"CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n"
|
||||
if auth_header:
|
||||
req += f"Proxy-Authorization: Basic {auth_header}\r\n"
|
||||
req += "\r\n"
|
||||
s.sendall(req.encode("ascii"))
|
||||
|
||||
# read headers
|
||||
resp = b""
|
||||
while b"\r\n\r\n" not in resp:
|
||||
chunk = s.recv(4096)
|
||||
if not chunk:
|
||||
break
|
||||
resp += chunk
|
||||
if len(resp) > 65536:
|
||||
break
|
||||
|
||||
first_line = resp.split(b"\r\n", 1)[0].decode("ascii", errors="ignore")
|
||||
if not first_line.startswith("HTTP/") or ("200" not in first_line):
|
||||
raise RuntimeError(f"Proxy CONNECT failed: {first_line}")
|
||||
|
||||
ctx = ssl.create_default_context()
|
||||
with ctx.wrap_socket(s, server_hostname=host) as ss:
|
||||
ss.settimeout(15)
|
||||
der = ss.getpeercert(True)
|
||||
return ssl.DER_cert_to_PEM_cert(der)
|
||||
except Exception:
|
||||
s.close()
|
||||
raise
|
||||
|
||||
logger.debug("OpenSSL not found; using Python ssl fallback (proxy-aware)")
|
||||
# Prefer HTTPS_PROXY then HTTP_PROXY
|
||||
proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy") or os.environ.get("HTTP_PROXY") or os.environ.get("http_proxy")
|
||||
try:
|
||||
ctx = ssl.create_default_context()
|
||||
s = socket.socket(socket.AF_INET)
|
||||
with ctx.wrap_socket(s, server_hostname=host) as ss:
|
||||
ss.settimeout(15)
|
||||
ss.connect((host, port))
|
||||
der = ss.getpeercert(True)
|
||||
pem = ssl.DER_cert_to_PEM_cert(der)
|
||||
logger.debug("Python ssl fallback succeeded")
|
||||
return pem
|
||||
if proxy:
|
||||
logger.debug(f"Using proxy for ssl check: {proxy}")
|
||||
pem = fetch_via_proxy(proxy)
|
||||
else:
|
||||
ctx = ssl.create_default_context()
|
||||
s = socket.socket(socket.AF_INET)
|
||||
with ctx.wrap_socket(s, server_hostname=host) as ss:
|
||||
ss.settimeout(15)
|
||||
ss.connect((host, port))
|
||||
der = ss.getpeercert(True)
|
||||
pem = ssl.DER_cert_to_PEM_cert(der)
|
||||
|
||||
logger.debug("Python ssl fallback succeeded")
|
||||
return pem
|
||||
except Exception as e:
|
||||
logger.exception("Python SSL fallback failed")
|
||||
return f"Python SSL fallback failed: {e}\n"
|
||||
|
||||
Reference in New Issue
Block a user