Commit Graph

2 Commits

Author SHA1 Message Date
rcourtman 64fb3d198d fix(securityutil): dial every permitted resolved IP instead of pinning the first
The restricted outbound dialer resolved a hostname, validated the results,
then dialed only the first permitted IP. When localhost resolves to ::1
ahead of 127.0.0.1 and the target service binds only 127.0.0.1 (Ollama's
default), the dial failed with 'connect: connection refused' even though
curl and browsers connect fine via address-family fallback. This broke the
AI provider setup quickstart: the blessed http://localhost:11434 Ollama URL
failed Pulse's connection test on IPv6-first hosts (setup-friction class of
#847/#1003).

Try each permitted IP from the validating resolution in order until one
connects. Every candidate is still validated against the outbound policy
from the same resolution, so the DNS-rebinding pinning guarantee is
unchanged. Fixes all consumers of the shared client (Ollama provider, OIDC,
SSO, connection probes, availability poller); api-contracts wording updated
to match. Live-verified against Ollama 0.32.1 bound to 127.0.0.1 only:
TestConnection via http://localhost:11434 failed before, passes after.
2026-07-17 23:35:45 +01:00
rcourtman b6f7091952 fix: block SSRF bypass through HTTP proxy in restricted outbound client
NewRestrictedOutboundHTTPClient validates target IPs via a custom
DialContext, but when an HTTP proxy is configured (HTTP_PROXY env var)
DialContext only validates the proxy's address — the actual target host
is never checked. This allows requests to cloud metadata service
addresses (169.254.169.254) and other blocked IPs through the proxy.

Add a restrictedRoundTripper wrapper that validates the request URL
hostname against resolvePermittedOutboundIP before forwarding. This
provides defense-in-depth that works regardless of proxy configuration,
while the existing DialContext guard continues to prevent DNS rebinding
for direct connections.
2026-06-26 13:05:23 +01:00