micro/go-micro · error

push callback host %q resolves to a blocked address %s

Error message

push callback host %q resolves to a blocked address %s

What it means

This is the SSRF protection core of defaultPushURLPolicy: after resolving the callback host, each returned IP is checked with blockedPushIP, and this error is thrown if any address is loopback, private, link-local, multicast, or unspecified. It prevents push notifications from being aimed at internal infrastructure (localhost, metadata services, RFC1918 ranges).

Source

Thrown at gateway/a2a/pushsecurity.go:51

	switch u.Scheme {
	case "http", "https":
	default:
		return fmt.Errorf("push callback scheme %q not allowed (want http or https)", u.Scheme)
	}
	host := u.Hostname()
	if host == "" {
		return fmt.Errorf("push callback url has no host")
	}
	ips, err := resolvePushHost(host)
	if err != nil {
		return fmt.Errorf("push callback host %q: %w", host, err)
	}
	if len(ips) == 0 {
		return fmt.Errorf("push callback host %q did not resolve", host)
	}
	for _, ip := range ips {
		if blockedPushIP(ip) {
			return fmt.Errorf("push callback host %q resolves to a blocked address %s", host, ip)
		}
	}
	return nil
}

func resolvePushHost(host string) ([]net.IP, error) {
	if ip := net.ParseIP(host); ip != nil {
		return []net.IP{ip}, nil
	}
	return pushLookupIP(host)
}

// blockedPushIP reports whether ip is one an outbound push callback must not
// reach: loopback, private (RFC1918 / ULA), link-local (incl. 169.254.169.254
// cloud metadata), multicast, or the unspecified address.
func blockedPushIP(ip net.IP) bool {
	return ip == nil ||
		ip.IsLoopback() ||

View on GitHub (pinned to 24529f1404)

Solutions

  1. Use a publicly routable callback host; expose the webhook receiver on a public address.
  2. If internal delivery is genuinely required, deploy an approved public proxy/relay in front of the internal service.
  3. Never accept raw user URLs for push callbacks without your own allowlist of webhook domains.
  4. If this fires unexpectedly on a legitimate host, inspect what the name resolves to (possible rebinding or misconfigured DNS).

Example fix

// before
callback := "http://localhost:9090/a2a/push" // blocked
// after
callback := "https://push.example.com/a2a/push" // public, routable
Defensive patterns

Strategy: validation

Validate before calling

addrs, _ := net.LookupHost(host)
for _, a := range addrs {
	ip := net.ParseIP(a)
	if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsUnspecified() {
		return fmt.Errorf("callback host %q resolves to private address %s", host, ip)
	}
}

Type guard

func isPublicIP(ip net.IP) bool {
	return ip != nil && !ip.IsLoopback() && !ip.IsPrivate() &&
		!ip.IsLinkLocalUnicast() && !ip.IsLinkLocalMulticast() && !ip.IsUnspecified()
}

Prevention

When it happens

Trigger: Calling SetPushNotificationConfig with a callback URL whose host resolves to 127.0.0.1, ::1, 10.x/172.16.x/192.168.x, 169.254.x (including cloud metadata 169.254.169.254), 0.0.0.0, or a multicast address.

Common situations: Attacker-supplied webhook URL in a multi-tenant app attempting SSRF against the gateway; developer pointing callbacks at a locally running service in a production deployment; DNS rebinding where a public name resolves to a private IP.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/08d128a4032cde16. Report an issue: GitHub.