Tencent/WeKnora · error

connection blocked: hostname suffix %s is restricted

Error message

connection blocked: hostname suffix %s is restricted

What it means

SSRFSafeDialContext blocked the connection because the lowercased hostname ends with one of the restrictedHostSuffixes. Suffix matching catches whole classes of forbidden names (e.g. ".internal", ".local", or metadata suffixes) without enumerating each host. This is a deliberate policy denial to stop lookalike subdomains from reaching restricted zones.

Source

Thrown at internal/utils/security.go:822

			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}
		return dialer.DialContext(ctx, network, addr)
	}
	if restrictedPorts[port] {
		return nil, fmt.Errorf("connection blocked: port %s is restricted", port)
	}

	// Check if the host is a restricted hostname
	hostLower := strings.ToLower(host)
	for _, restricted := range restrictedHostnames {
		if hostLower == restricted {
			return nil, fmt.Errorf("connection blocked: hostname %s is restricted", host)
		}
	}
	for _, suffix := range restrictedHostSuffixes {
		if strings.HasSuffix(hostLower, suffix) {
			return nil, fmt.Errorf("connection blocked: hostname suffix %s is restricted", suffix)
		}
	}

	// Resolve the hostname once, validate every answer, and then dial one of
	// those exact IPs. Dialing the original hostname here would make the
	// standard dialer resolve it a second time, leaving a DNS-rebinding window
	// between validation and connection establishment.
	ips, err := net.DefaultResolver.LookupIPAddr(ctx, host)
	if err != nil {
		return nil, fmt.Errorf("DNS resolution failed for %s: %w", host, err)
	}
	if len(ips) == 0 {
		return nil, fmt.Errorf("DNS resolution returned no addresses for %s", host)
	}

	// Validate all resolved IPs
	for _, ipAddr := range ips {
		if restricted, reason := isRestrictedIP(ipAddr.IP); restricted {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Check restrictedHostSuffixes in internal/utils/security.go to see which suffix matched, then use a hostname outside the restricted zone.
  2. If the suffix-restricted host is genuinely trusted, add the full host to the SSRF whitelist to bypass dial-time checks.
  3. Reconfigure internal DNS/service names to an allowed zone (e.g. ".example.internal" if only ".internal" is blocked).
  4. As a last resort, use a non-guarded dialer for that specific connection, accepting the loss of DNS-rebinding protection.

Example fix

// before
conn, err := utils.SSRFSafeDialContext(ctx, "tcp", "billing.svc.cluster.local:443") // ".local" suffix restricted

// after
conn, err := utils.SSRFSafeDialContext(ctx, "tcp", "billing.internal.example.com:443") // or whitelist the host
Defensive patterns

Strategy: validation

Validate before calling

host, _, _ := net.SplitHostPort(addr)
h := strings.ToLower(host)
for _, suffix := range []string{".internal", ".local" /* + restrictedHostSuffixes list */} {
    if strings.HasSuffix(h, suffix) {
        return fmt.Errorf("hostname %s uses restricted suffix %s; rename or whitelist", host, suffix)
    }
}

Try / catch

conn, err := utils.SSRFSafeDialContext(ctx, "tcp", addr)
if err != nil && strings.Contains(err.Error(), "hostname suffix") {
    return nil, fmt.Errorf("destination in restricted DNS zone: %w", err)
}

Prevention

When it happens

Trigger: Dialing through SSRFSafeDialContext / SSRFSafeGRPCDialer with a host whose name ends in a restricted suffix (e.g. "myservice.internal" when ".internal" is restricted), on the non-whitelisted path. TestSSRFSafeDialContextRejectsRestrictedPortAtFinalSink drives the final-sink code path that performs this check.

Common situations: Internal service DNS zones that collide with the restricted suffix list; Kubernetes cluster-local names like "svc.cluster.local" hitting a ".local" restriction; renaming services into a suffix the library treats as forbidden.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/e2c1c9c3ab495ee0. Report an issue: GitHub.