microsoft/semantic-kernel · error · FunctionExecutionException

The request URI '{url}' is not allowed: host resolves to a {

Error message

The request URI '{url}' is not allowed: host resolves to a {category} address ({address}), which is blocked by default to prevent Server-Side Request Forgery (SSRF). To allow this URL, add it to server_url_validation_allowed_base_urls or set allow_private_network_access=True.

What it means

Thrown when the host resolves to a non-public IP address. Categories blocked: loopback (127.0.0.0/8), RFC1918 private (10/8, 172.16/12, 192.168/16), link-local (169.254/16), unspecified (0/8), carrier-grade NAT, benchmarking, reserved, multicast, and IPv6 loopback/ULA/link-local/multicast/reserved. This is the core SSRF guard.

Source

Thrown at python/semantic_kernel/connectors/openapi_plugin/server_url_validator.py:186

        ) from exc

    addresses: list[ipaddress.IPv4Address | ipaddress.IPv6Address] = []
    seen_addresses: set[str] = set()
    for family, _, _, _, sockaddr in addr_info:
        if family not in (socket.AF_INET, socket.AF_INET6):
            continue
        address = ipaddress.ip_address(sockaddr[0])
        address_string = str(address)
        if address_string not in seen_addresses:
            addresses.append(address)
            seen_addresses.add(address_string)
    return addresses


def _ensure_public_address(url: str, address: ipaddress.IPv4Address | ipaddress.IPv6Address) -> None:
    blocked, category = try_categorize_non_public_address(address)
    if blocked:
        raise FunctionExecutionException(
            f"The request URI '{url}' is not allowed: host resolves to a {category} address ({address}), "
            "which is blocked by default to prevent Server-Side Request Forgery (SSRF). "
            "To allow this URL, add it to server_url_validation_allowed_base_urls or set "
            "allow_private_network_access=True."
        )


def _try_classify_ipv4(address: ipaddress.IPv4Address) -> tuple[bool, str]:
    b0, b1, b2, _ = address.packed

    if b0 == 0:
        return True, "unspecified"
    if b0 == 10:
        return True, "private (RFC1918)"
    if b0 == 127:
        return True, "loopback"
    if b0 == 169 and b1 == 254:
        return True, "link-local"

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Add the specific trusted base URL to allowed_base_urls to bypass all host checks for that destination
  2. Set allow_private_network_access=True to skip the public-address check entirely (only when you fully trust and control the target)
  3. Ensure you are hitting a public hostname that resolves to a genuinely public IP
  4. Never allow untrusted/user-supplied URLs to reach internal or metadata addresses

Example fix

# before
options = ServerUrlValidationOptions()  # default: blocks private addresses
await validate_server_url('https://internal.svc.cluster.local/api', options)  # raises 1507

# after - explicitly trust the internal base
options = ServerUrlValidationOptions(allowed_base_urls=['https://internal.svc.cluster.local/api'])
await validate_server_url('https://internal.svc.cluster.local/api/run', options)
Defensive patterns

Strategy: validation

Validate before calling

import ipaddress, socket

def host_is_public(host: str) -> bool:
    # if host is already an IP
    try:
        ip = ipaddress.ip_address(host)
    except ValueError:
        try:
            infos = socket.getaddrinfo(host, None)
        except OSError:
            return False
        ips = [ipaddress.ip_address(i[4][0]) for i in infos if i[0] in (socket.AF_INET, socket.AF_INET6)]
    else:
        ips = [ip]
    for ip in ips:
        if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_unspecified or ip.is_reserved or ip.is_multicast:
            return False
    return True

if not host_is_public(urlparse(url).hostname):
    # either reject or add the base url to allowed_base_urls / set allow_private_network_access
    ...

Try / catch

try:
    await validate_server_url(url, options)
except FunctionExecutionException as e:
    if 'blocked by default to prevent Server-Side Request Forgery' in str(e):
        # add the trusted base url to allowed_base_urls, or set allow_private_network_access=True
        ...

Prevention

When it happens

Trigger: Server URL host resolves to 127.0.0.1, 10.x, 192.168.x, 169.254.169.254 (cloud metadata), fc00::/7, ::1, etc.; or a DNS rebinding attack that flips a public-looking name to a private IP at request time.

Common situations: Pointing at localhost/127.0.0.1 for local dev; reaching an internal service on a private IP; cloud metadata endpoint abuse (169.254.169.254); DNS rebinding from an attacker-controlled hostname.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/f84525ea8cb38e8f. Report an issue: GitHub.