mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
* Add OpenAI-compatible API option in model configuration
Backend-only cherry-pick from 20617db37a.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: harden the base url settings in SaaS mode
---------
Co-authored-by: Chris Briddock <briddockchristopher@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
66 lines
1.9 KiB
Python
66 lines
1.9 KiB
Python
import ipaddress
|
|
import socket
|
|
from urllib.parse import urlparse
|
|
|
|
from api.constants import DEPLOYMENT_MODE
|
|
|
|
_CGNAT_NETWORK = ipaddress.ip_network("100.64.0.0/10")
|
|
|
|
|
|
def validate_user_configured_service_url(
|
|
url: str,
|
|
*,
|
|
field_name: str,
|
|
) -> None:
|
|
"""Restrict user-configured service URLs in hosted deployments.
|
|
|
|
OSS deployments commonly point model services at localhost or private LAN
|
|
hosts. SaaS deployments must not allow users to make Dograh infrastructure
|
|
connect to private/internal network locations.
|
|
"""
|
|
if DEPLOYMENT_MODE == "oss":
|
|
return
|
|
|
|
parsed = urlparse(url)
|
|
if parsed.scheme not in {"http", "https", "ws", "wss"} or not parsed.hostname:
|
|
raise ValueError(f"{field_name} must be an http, https, ws, or wss URL")
|
|
|
|
hostname = parsed.hostname
|
|
if hostname.lower() == "localhost":
|
|
raise ValueError(f"{field_name} cannot point to localhost in SaaS mode")
|
|
|
|
for ip in _resolve_hostname_ips(hostname, parsed.port):
|
|
if _is_blocked_saas_service_ip(ip):
|
|
raise ValueError(
|
|
f"{field_name} must resolve to a public IP address in SaaS mode"
|
|
)
|
|
|
|
|
|
def _resolve_hostname_ips(
|
|
hostname: str, port: int | None
|
|
) -> list[ipaddress.IPv4Address | ipaddress.IPv6Address]:
|
|
try:
|
|
return [ipaddress.ip_address(hostname)]
|
|
except ValueError:
|
|
pass
|
|
|
|
try:
|
|
addr_infos = socket.getaddrinfo(hostname, port, type=socket.SOCK_STREAM)
|
|
except socket.gaierror as e:
|
|
raise ValueError("Could not resolve service URL hostname") from e
|
|
|
|
return [ipaddress.ip_address(addr_info[4][0]) for addr_info in addr_infos]
|
|
|
|
|
|
def _is_blocked_saas_service_ip(
|
|
ip: ipaddress.IPv4Address | ipaddress.IPv6Address,
|
|
) -> bool:
|
|
return (
|
|
ip.is_private
|
|
or ip.is_loopback
|
|
or ip.is_link_local
|
|
or ip.is_multicast
|
|
or ip.is_reserved
|
|
or ip.is_unspecified
|
|
or (ip.version == 4 and ip in _CGNAT_NETWORK)
|
|
)
|