langchain-ai/langchain · error · SSRFBlockedError
Kubernetes internal DNS
Error message
Kubernetes internal DNS
What it means
Raised by `validate_hostname` when the hostname ends with the Kubernetes-internal DNS suffix (`_K8S_SUFFIX`, i.e. `.svc`, covering `*.default.svc`, `*.kube-system.svc` cluster-local service names) and `policy.block_k8s_internal` is enabled. In-cluster service DNS is reachable only from inside the cluster and often unauthenticated, so SSRF policy blocks it like any other internal range.
Source
Thrown at libs/core/langchain_core/_security/_policy.py:232
def validate_hostname(hostname: str, policy: SSRFPolicy) -> None:
"""Validate a hostname against the SSRF policy.
Raises SSRFBlockedError if the hostname is blocked.
"""
lower = hostname.lower()
if policy.block_localhost and lower in _LOCALHOST_NAMES:
msg = "localhost address"
raise SSRFBlockedError(msg)
if policy.block_cloud_metadata and lower in _CLOUD_METADATA_HOSTNAMES:
msg = "cloud metadata endpoint"
raise SSRFBlockedError(msg)
if policy.block_k8s_internal and lower.endswith(_K8S_SUFFIX):
msg = "Kubernetes internal DNS"
raise SSRFBlockedError(msg)
def _effective_allowed_hosts(policy: SSRFPolicy) -> frozenset[str]:
"""Return allowed_hosts, augmented for local environments."""
extra: set[str] = set()
if os.environ.get("LANGCHAIN_ENV", "").startswith("local"):
extra.update({"localhost", "testserver"})
if extra:
return policy.allowed_hosts | frozenset(extra)
return policy.allowed_hosts
async def validate_url(url: str, policy: SSRFPolicy = DEFAULT_SSRF_POLICY) -> None:
"""Validate a URL against the SSRF policy, including DNS resolution.
This is the primary entry-point for async code paths. It delegates
scheme/hostname/allowed-hosts checks to `validate_url_sync`, then
resolves DNS and validates every resolved IP.View on GitHub (pinned to e32fa9a52e)
Solutions
- If the in-cluster target is trusted, construct the policy with that service hostname in `allowed_hosts` (or set `block_k8s_internal=False` only for that internal client, never for user-supplied URLs).
- Prefer in-cluster service calls over the SSRF-guarded fetch path — call the service client directly rather than through URL validation.
- Namespace the relaxation: keep the default policy for external/user URLs and a separate permissive policy for known-internal base URLs.
Example fix
# before
validate_url_sync('http://embeddings.svc:8080/embed', DEFAULT_SSRF_POLICY)
# SSRFBlockedError: Kubernetes internal DNS
# after
internal_policy = SSRFPolicy(allowed_hosts={'embeddings.default.svc'})
validate_url_sync('http://embeddings.svc:8080/embed', internal_policy) Defensive patterns
Strategy: try-catch
Validate before calling
def is_k8s_svc_host(url: str) -> bool:
host = (urlparse(url).hostname or "").lower()
return host.endswith(".svc") or host.endswith(".svc.cluster.local") Try / catch
from langchain_core._security._policy import SSRFBlockedError
try:
validate_url_sync(url, policy)
except SSRFBlockedError as e:
if "Kubernetes" in str(e) and url_host in settings.trusted_internal_hosts:
return validate_url_sync(url, internal_policy)
raise Prevention
- Keep two policies: strict defaults for external/user URLs, a permissive one scoped to configured internal service hosts.
- Call in-cluster services via their client library instead of the SSRF-guarded URL fetch when possible.
When it happens
Trigger: `validate_hostname('prometheus.monitoring.svc', policy)`, `validate_url_sync('http://my-service.default.svc:8080/api')`, or any fetched URL whose host ends in `.svc` under the default `block_k8s_internal=True`. A crafted name like `evil-attacker.example.svc.attacker.com` is not caught (only `endswith` on the exact suffix is checked) but ordinary cluster names are.
Common situations: Deploying a langchain service inside Kubernetes that legitimately needs to call another in-cluster service (internal embeddings API, feature store) through a URL-validated fetch; Helm-chart env vars commonly use `<service>.<namespace>.svc` addresses and suddenly fail SSRF validation after the guard ships.
Related errors
- Failed to resolve hostname '{hostname}': {e}
- invalid IP address
- private IP range
- blocked CIDR
- localhost address
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/8c7f9f9e8b5d5f38.
Report an issue: GitHub.