Skip to content

Commit a54878b

Browse files
committed
refac
1 parent 8b9e28b commit a54878b

2 files changed

Lines changed: 75 additions & 3 deletions

File tree

backend/open_webui/env.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,65 @@ def parse_section(section):
498498
WEBSOCKET_EVENT_CALLER_TIMEOUT = 300
499499

500500

501+
import ssl as _ssl
502+
503+
504+
# Dedicated env var for a custom CA bundle file path. When set, this is
505+
# used as the default CA bundle for all outbound HTTPS connections that
506+
# have SSL verification enabled (i.e. when their per-connection SSL env
507+
# var is ``"True"``). Per-connection overrides (setting the SSL env var
508+
# to a path directly) take precedence over this global fallback.
509+
#
510+
# This follows the industry convention of ``SSL_CERT_FILE`` / ``REQUESTS_CA_BUNDLE``
511+
# but is scoped to Open WebUI to avoid interfering with system-level settings.
512+
AIOHTTP_CLIENT_SSL_CERT_FILE = os.getenv('AIOHTTP_CLIENT_SSL_CERT_FILE', '').strip()
513+
514+
515+
def _build_ssl_context_from_file(path: str) -> '_ssl.SSLContext | None':
516+
"""Create an SSLContext from a CA bundle file, or None if invalid."""
517+
if not path:
518+
return None
519+
if not os.path.isfile(path):
520+
log.warning(
521+
'SSL CA bundle path does not exist: %r, ignoring',
522+
path,
523+
)
524+
return None
525+
ctx = _ssl.create_default_context(cafile=path)
526+
log.info('Using custom SSL CA bundle: %s', path)
527+
return ctx
528+
529+
530+
# Pre-built SSLContext from the dedicated env var (cached once at startup).
531+
_GLOBAL_SSL_CONTEXT = _build_ssl_context_from_file(AIOHTTP_CLIENT_SSL_CERT_FILE)
532+
533+
534+
def _parse_ssl_env(value: str) -> 'bool | _ssl.SSLContext':
535+
"""Parse an SSL env var into a bool or SSLContext.
536+
537+
- ``"true"`` → uses ``AIOHTTP_CLIENT_SSL_CERT_FILE`` context if set,
538+
otherwise ``True`` (default SSL verification via certifi)
539+
- ``"false"`` → ``False`` (no verification)
540+
- ``"/path/to/ca-bundle.crt"`` → ``SSLContext`` loading that CA file
541+
(takes precedence over ``AIOHTTP_CLIENT_SSL_CERT_FILE``)
542+
543+
This allows users with corporate or internal CAs to point Open WebUI
544+
at a custom CA bundle without disabling verification entirely.
545+
"""
546+
lower = value.strip().lower()
547+
if lower == 'true':
548+
# Use the global dedicated CA bundle if configured, otherwise default
549+
return _GLOBAL_SSL_CONTEXT if _GLOBAL_SSL_CONTEXT is not None else True
550+
if lower == 'false':
551+
return False
552+
# Treat as a file path to a CA bundle (per-connection override)
553+
ctx = _build_ssl_context_from_file(value.strip())
554+
if ctx is not None:
555+
return ctx
556+
# Path was invalid — fall back to default
557+
return _GLOBAL_SSL_CONTEXT if _GLOBAL_SSL_CONTEXT is not None else True
558+
559+
501560
REQUESTS_VERIFY = os.getenv('REQUESTS_VERIFY', 'True').lower() == 'true'
502561

503562
_aiohttp_timeout_raw = os.getenv('AIOHTTP_CLIENT_TIMEOUT', '')
@@ -507,7 +566,10 @@ def parse_section(section):
507566
AIOHTTP_CLIENT_TIMEOUT = 300
508567

509568

510-
AIOHTTP_CLIENT_SESSION_SSL = os.getenv('AIOHTTP_CLIENT_SESSION_SSL', 'True').lower() == 'true'
569+
# SSL verification for general outbound requests (OpenAI, OAuth, etc.).
570+
# Accepts "True", "False", or a path to a CA bundle file.
571+
# When "True", falls back to AIOHTTP_CLIENT_SSL_CERT_FILE if set.
572+
AIOHTTP_CLIENT_SESSION_SSL = _parse_ssl_env(os.getenv('AIOHTTP_CLIENT_SESSION_SSL', 'True'))
511573

512574
# When False (default), outbound HTTP requests do not follow 3xx redirects.
513575
AIOHTTP_CLIENT_ALLOW_REDIRECTS = os.getenv('AIOHTTP_CLIENT_ALLOW_REDIRECTS', 'False').lower() == 'true'
@@ -533,7 +595,12 @@ def parse_section(section):
533595
AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER_DATA = 10
534596

535597

536-
AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL = os.getenv('AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL', 'True').lower() == 'true'
598+
# SSL verification for tool server connections specifically.
599+
# Accepts "True", "False", or a path to a CA bundle file.
600+
# When "True", falls back to AIOHTTP_CLIENT_SSL_CERT_FILE if set.
601+
AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL = _parse_ssl_env(
602+
os.getenv('AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL', 'True')
603+
)
537604

538605
AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER = os.getenv('AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER', '')
539606

backend/open_webui/utils/mcp/client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ def _build_httpx_client(headers=None, timeout=None, auth=None, verify=True):
4444

4545

4646
def create_httpx_client(headers=None, timeout=None, auth=None):
47-
return _build_httpx_client(headers=headers, timeout=timeout, auth=auth, verify=True)
47+
# AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL may be True, False, or an
48+
# ssl.SSLContext (when a custom CA bundle path is configured).
49+
# httpx's verify= accepts bool | str | ssl.SSLContext, so all three work.
50+
ssl_setting = AIOHTTP_CLIENT_SESSION_TOOL_SERVER_SSL
51+
verify = ssl_setting if ssl_setting is not True else True
52+
return _build_httpx_client(headers=headers, timeout=timeout, auth=auth, verify=verify)
4853

4954

5055
def create_insecure_httpx_client(headers=None, timeout=None, auth=None):

0 commit comments

Comments
 (0)