siyuan-note/siyuan · error
host has no public IP:
Error message
host has no public IP:
What it means
When the agent dialer gets a hostname, it resolves all IPs, silently skips private ones, and dials only public records. If every resolved record is private, zero dial attempts happen and this 'host has no public IP' error is returned. (If public records existed but every dial failed, the last dial error is returned instead — this message specifically means no public record existed.)
Source
Thrown at kernel/util/net.go:209
ips, err := net.DefaultResolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
var lastErr error
for _, ipAddr := range ips {
if isPrivateIP(ipAddr.IP) {
continue
}
conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(ipAddr.IP.String(), port))
if err == nil {
return conn, nil
}
lastErr = err
}
if lastErr != nil {
return nil, lastErr
}
return nil, errors.New("host has no public IP: " + host)
}
}
// isPrivateIP 判断 IP 是否为私网地址,含内嵌私网 IPv4 的 IPv6 过渡地址(NAT64、6to4、Teredo、IPv4 兼容)。
// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-qq8m-8p8v-x4xg
// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-rg26-cg95-gq6p
func isPrivateIP(ip net.IP) bool {
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() ||
ip.IsPrivate() || ip.IsUnspecified() || ip.IsMulticast() {
return true
}
// Go 标准库的分类方法不识别 IPv6 过渡地址,需按 RFC 内嵌格式提取其中的 IPv4 后再递归判断。
if ip4 := extractEmbeddedIPv4(ip); nil != ip4 && !ip4.Equal(ip) {
return isPrivateIP(ip4)
}
return false
}
View on GitHub (pinned to afa823b6b4)
Solutions
- Verify resolution from this machine (dig +short <host>) and use a name with at least one public record
- Expose the internal service publicly (tunnel, reverse proxy) if the agent genuinely needs it
- Check /etc/hosts and VPN DNS for private overrides of the name
- Accept the restriction: pointing the agent at private networks is blocked by design (GHSA advisories)
Example fix
// before
resp, err := ssrfClient.Get("http://intranet-wiki.corp/page") // 'host has no public IP: intranet-wiki.corp'
// after — pre-check and use a public mirror
if err := util.CheckHostSSRF("intranet-wiki.corp"); err != nil {
return errors.New("target resolves only to private IPs; use the public mirror")
}
resp, err := ssrfClient.Get("https://wiki.example.com/page") Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(rawURL)
if err != nil {
return err
}
ips, err := net.LookupIP(u.Hostname())
if err != nil {
return err
}
hasPublic := false
for _, ip := range ips {
if !ip.IsPrivate() && !ip.IsLoopback() && !ip.IsLinkLocalUnicast() {
hasPublic = true
}
}
if !hasPublic {
return fmt.Errorf("%s resolves only to private IPs; agent access blocked", u.Hostname())
} Type guard
func resolvesToPublicIP(host string) bool {
ips, err := net.LookupIP(host)
if err != nil {
return false
}
for _, ip := range ips {
if !ip.IsPrivate() && !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsUnspecified() {
return true
}
}
return false
} Try / catch
conn := ssrfSafeClient.Get(url)
if err != nil {
if strings.Contains(err.Error(), "no public IP") {
// DNS has no public record: fix the hostname or expose the service; do not retry
}
} Prevention
- Check DNS from the kernel machine (dig +short) for every host agents will fetch
- Avoid intranet hostnames, docker service names, and *.local in agent URLs
- Watch for /etc/hosts and VPN split-DNS overrides turning public names private
- Publish internal services through tunnels when agents must consume them
When it happens
Trigger: Agent fetching a hostname whose DNS returns only private A/AAAA records: intranet names, docker-service names like http://db:5432, *.local, split-horizon DNS, a public name overridden to a LAN IP by /etc/hosts or VPN DNS, or 'localhost' itself.
Common situations: Corporate intranet hostnames; docker-compose service discovery; VPN split DNS; captive portals rewriting DNS; hosts-file overrides during testing.
Related errors
- failed to resolve host:
- host has no public IP: %s
- access to private/internal IP is prohibited
- host has no IP address:
- ip address [%s] is prohibited
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/d55c3f11d16f37f8.
Report an issue: GitHub.