Kroki diagram proxy fails with private IP Kroki URLs due to inconsistent SSRF filtering between Rails and Workhorse
## Summary When Kroki is configured with a private/internal IP address (e.g. `http://10.88.0.3:8000`), the Kroki URL saves successfully in Admin settings but diagrams fail to render with a broken image when the diagram proxy is enabled. The Workhorse log shows: ``` SendURL: Do request: Get "http://10.88.0.3:8000/nwdiag/svg/...": dial tcp 10.88.0.3:8000: IP 10.88.0.3 is not allowed: private IPs are not allowed ``` ## Root Cause There are two inconsistent SSRF filtering layers: **1. Rails `UrlBlocker` (URL validation on save)** In `app/models/application_setting.rb`, `parsed_kroki_url` calls `UrlBlocker.validate!` without passing `allow_local_network`: ```ruby def parsed_kroki_url @parsed_kroki_url ||= Gitlab::HTTP_V2::UrlBlocker.validate!( kroki_url, schemes: %w[http https], enforce_sanitization: true, deny_all_requests_except_allowed: Gitlab::CurrentSettings.deny_all_requests_except_allowed?, outbound_local_requests_allowlist: Gitlab::CurrentSettings.outbound_local_requests_whitelist)[0] ``` The default for `allow_local_network` in `UrlBlocker` is `true`, so private IP addresses like `10.88.0.3` are **allowed through** at save time. **2. Workhorse SSRF filter (render time)** In `app/controllers/banzai/diagram_proxy_controller.rb`, `send_url` is called with `ssrf_filter: true` but no `allowed_endpoints`: ```ruby headers.store(*Gitlab::Workhorse.send_url(url, allow_redirects: true, ssrf_filter: true)) ``` Workhorse has a hardcoded list of private network CIDRs in `workhorse/internal/transport/transport.go` (including `10.0.0.0/8`) and blocks the request at render time. The outbound allowlist configured in Admin settings is never forwarded to Workhorse. ## Impact - Admins running Kroki on an internal/private network IP cannot use the diagram proxy, even if the IP is in the outbound allowlist - This includes admins who deploy Kroki on k8s and use `svc.cluster.local` hosts which are private IPs. As a result this feature does not work in k8s. - The failure is silent from the admin's perspective — the URL saves fine but diagrams silently fail with a broken image icon - The only working configuration is `localhost`/`127.0.0.1` because Workhorse has a separate `AllowLocalhost` flag that Rails does pass ## Steps to Reproduce 1. Run Kroki locally: `podman run -d --name kroki -p 8000:8000 docker.io/yuzutech/kroki` 2. Get the container IP: `podman inspect kroki --format '{{.NetworkSettings.IPAddress}}'` (e.g. `10.88.0.3`) 3. In **Admin > Settings > General > Kroki**, enable Kroki and set URL to `http://10.88.0.3:8000` 4. Enable the Kroki diagram proxy 5. Add a diagram to an issue or wiki page: ```` ```graphviz digraph { a -> b } ``` ```` 6. Observe broken image icon 7. Check Workhorse log: `sudo gitlab-ctl tail gitlab-workhorse/current` — see `private IPs are not allowed` error ## Expected Behaviour If a private IP is in the outbound local requests allowlist, the diagram proxy should be able to reach it. The `DiagramProxyController` should forward the allowlist to Workhorse as `allowed_endpoints`, similar to how the dependency proxy does it: ```ruby # ee/lib/api/concerns/dependency_proxy/packages_helpers.rb Gitlab::Workhorse.send_url( url, ssrf_filter: true, allowed_endpoints: allowed_endpoints ) ``` ## Proposed Fix In `app/controllers/banzai/diagram_proxy_controller.rb`, pass the outbound allowlist to `send_url`: ```ruby headers.store(*Gitlab::Workhorse.send_url( url, allow_redirects: true, ssrf_filter: true, allowed_endpoints: Gitlab::CurrentSettings.outbound_local_requests_whitelist )) ``` ## Environment - GitLab 18.10.4 - Omnibus installation - Kroki running in Podman container on private network IP
issue