lfnovo/open-notebook · critical · ValueError

Hostname '{hostname}' resolves to a link-local address (169.

Error message

Hostname '{hostname}' resolves to a link-local address (169.254.x.x) which is not allowed for security reasons. These addresses are used for cloud metadata endpoints.

What it means

A user-supplied hostname (not a raw IP) resolved via DNS to a link-local address (169.254.x.x or fe80::/10, including IPv4-mapped IPv6 forms). These ranges host cloud metadata services (AWS/GCP/Azure IMDS), so reaching them via a configurable URL is a classic SSRF vector and is blocked. The message names the hostname because it was resolved, not written literally.

Source

Thrown at open_notebook/utils/url_validation.py:226

    return safe


def _reject_dangerous_ip(
    ip: "ipaddress.IPv4Address | ipaddress.IPv6Address",
    hostname: str,
    resolved: bool = False,
) -> None:
    """Raise ValueError if `ip` is a link-local or cloud-metadata address."""
    is_ipv4_mapped_link_local = (
        hasattr(ip, "ipv4_mapped") and ip.ipv4_mapped and ip.ipv4_mapped.is_link_local
    )

    # Block link-local addresses (169.254.x.x / fe80::/10) - used for cloud
    # metadata - including IPv4-mapped IPv6 addresses pointing to link-local
    # (e.g. ::ffff:169.254.169.254 bypasses IPv6 is_link_local check).
    if ip.is_link_local or is_ipv4_mapped_link_local:
        if resolved:
            raise ValueError(
                f"Hostname '{hostname}' resolves to a link-local address (169.254.x.x) "
                "which is not allowed for security reasons. These addresses are used "
                "for cloud metadata endpoints."
            )
        raise ValueError(
            "Link-local addresses (169.254.x.x) are not allowed for security reasons. "
            "These addresses are used for cloud metadata endpoints."
        )

    # Block AWS's IMDSv6 metadata address - a Unique Local Address, not
    # link-local, so it needs its own explicit check. Compare without scope
    # ID so scoped forms (fd00:ec2::254%eth0) cannot bypass the sentinel.
    is_aws_imds_v6 = (
        isinstance(ip, ipaddress.IPv6Address)
        and int(ip) == int(_AWS_IMDS_V6_ADDRESS)
    )
    if is_aws_imds_v6:
        if resolved:

View on GitHub (pinned to a7de90d38a)

Solutions

  1. Remove the hostname that resolves to 169.254.x.x from the provider config — it will never be allowed
  2. If you genuinely need a cloud API, use its public hostname (e.g. metrics or IMDS via the SDK's built-in mechanism), not a metadata IP
  3. Audit where the DNS record comes from and fix it if it was unintentional
Defensive patterns

Strategy: validation

Validate before calling

import ipaddress

def is_safe_public_target(host: str) -> bool:
    try:
        ip = ipaddress.ip_address(host)
    except ValueError:
        return True  # hostname; DNS-time check still applies
    return not (ip.is_link_local or ip.is_private is False and ip.is_reserved)

Try / catch

try:
    target = await prepare_pinned_http_target(url, provider)
except ValueError as e:
    if "link-local" in str(e) or "metadata" in str(e):
        log_security_event(url)  # potential SSRF attempt
    raise

Prevention

When it happens

Trigger: Setting a provider base_url to a hostname whose DNS resolves to 169.254.169.254 (e.g. a wildcard DNS service like nip.io/sslip.io style names, or an internal name that points at the metadata IP), then running discover_with_config or a connection test.

Common situations: Attempting to probe cloud instance metadata from the app; DNS rebinding-style SSRF tests; misconfigured internal DNS that maps a friendly name to a link-local address.

Related errors


AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27). Data as JSON: /api/errors/676ae4ffa5f9dbab. Report an issue: GitHub.