siyuan-note/siyuan · error
access to private/internal IP is prohibited
Error message
access to private/internal IP is prohibited
What it means
A second dialer variant in net.go resolves the address and, when the host is already a literal IP, refuses any private/internal IP unconditionally (no SafeMode toggle) — a stricter SSRF guard used for contexts that must only reach public hosts.
Source
Thrown at kernel/util/net.go:187
},
}
}
// ssrfSafeDialContext 返回智能体出站请求专用的拨号函数:拨号时自行解析主机名并拒绝私网地址,
// 同时直接连接解析出的公网 IP,使 CheckHostSSRF 的守卫结果与拨号目标一致,
// 从根上杜绝 DNS 重绑定导致的 TOCTOU 绕过。
// 与 SSRFSafeDialer 不同,本拨号函数不依赖 SafeMode,始终强制执行。
// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-x8gv-g2g3-65fj
func ssrfSafeDialContext(timeout time.Duration) func(ctx context.Context, network, addr string) (net.Conn, error) {
dialer := &net.Dialer{Timeout: timeout}
return func(ctx context.Context, network, addr string) (net.Conn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
if ip := net.ParseIP(host); ip != nil {
if isPrivateIP(ip) {
return nil, errors.New("access to private/internal IP is prohibited")
}
return dialer.DialContext(ctx, network, addr)
}
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
}View on GitHub (pinned to 8641553a1f)
Solutions
- Replace the private/literal-IP target with a public hostname
- Use the SafeMode-controlled dialer path instead if private access is legitimate
- Expose the internal service through a public gateway if external fetching is required
Example fix
// before
client.Get("http://192.168.1.10/api") // blocked
// after
client.Get("https://public.example.com/api") Defensive patterns
Strategy: validation
Validate before calling
// pre-check literal IPs before dialing
ip := net.ParseIP(host)
if ip != nil && (ip.IsPrivate() || ip.IsLoopback()) { return errors.New("private target") } Type guard
func isPublicLiteralIP(host string) bool {
ip := net.ParseIP(host)
return ip != nil && !ip.IsPrivate() && !ip.IsLoopback() && !ip.IsLinkLocalUnicast()
} Try / catch
if _, err := hardenedDial(ctx, "tcp", addr); err != nil {
if strings.Contains(err.Error(), "private/internal IP") {
return errors.New("this code path only supports public hosts")
}
} Prevention
- Never hardcode private IPs into URLs consumed by hardened fetch paths
- Use public DNS names for cross-network calls
- Remember IPv6 transition addresses (NAT64/6to4/Teredo) are checked too
When it happens
Trigger: Dialing a URL whose host is a literal private IP (127.0.0.1, 10.x, 192.168.x, 172.16-31.x, link-local, or IPv6 NAT64/6to4/Teredo embedding private IPv4) through this dialer.
Common situations: Local development pointing API endpoints at localhost; internal services behind LAN addresses being fetched by hardened code paths; IPv6 transition addresses embedding private IPv4.
Related errors
- ip address [%s] is prohibited
- host has no public IP: %s
- host has no public IP:
- failed to resolve host:
- access to private/internal IP is prohibited
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/d9e5bb4aff8b9d13.
Report an issue: GitHub.