# How the SSRF Allowlist Protects Against Proxy-Side Request Forgery in Caveman

> Learn how the SSRF allowlist in Caveman safeguards against proxy-side request forgery by validating outbound URLs against unsafe addresses and an environment-driven allowlist.

- Repository: [Julius Brussee/caveman](https://github.com/JuliusBrussee/caveman)
- Tags: deep-dive
- Published: 2026-09-04

---

**The SSRF allowlist in Caveman prevents proxy-side request forgery by validating all outbound URLs against a blocklist of unsafe addresses and an environment-driven allowlist, rejecting connections to internal networks unless explicitly permitted in self-hosted mode.**

The JuliusBrussee/caveman repository implements a hardened defense against Server-Side Request Forgery (SSRF) attacks through its dedicated `ssrf` package in [`shared/platform/ssrf/ssrf.go`](https://github.com/JuliusBrussee/caveman/blob/main/shared/platform/ssrf/ssrf.go). This system ensures that proxy-type providers—including Vertex, Bedrock, and GitHubApp integrations—cannot be exploited to access restricted internal services or private IP ranges. Understanding how the **SSRF allowlist** functions is essential for securing both managed multi-tenant deployments and self-hosted installations.

## Core SSRF Protection Mechanisms

### Blocklist Enforcement for Unsafe Destinations

At the foundation of the protection lies the `ssrf.ValidateHost` function, which automatically rejects connections to **loopback addresses** (127.0.0.0/8, ::1), **link-local networks** (169.254.0.0/16), and standard private ranges. When validation detects a forbidden destination, it returns specific errors such as "host … is blocked (loopback)" or "private address blocked in managed mode" before any TCP socket opens. This enforcement prevents attackers from using the proxy to scan local ports or access cloud metadata endpoints.

### The Allowlist Override System

While the default policy denies access to internal addresses, the `CAVE_SSRF_ALLOWLIST` environment variable provides a controlled escape hatch parsed by the `allowListSuggestion` function. This allowlist accepts hostnames or host:port combinations that bypass blocklist restrictions. However, this override operates exclusively in **self-hosted mode** using `ssrf.SelfHostedConfig`. In **managed mode**, the system silently ignores the allowlist, ensuring that tenant workloads cannot circumvent network isolation to reach internal infrastructure.

## Preventing Proxy-Side Request Forgery

The SSRF architecture counters proxy-side request forgery through a three-layer defense that inspects every outbound HTTP request:

1. **Wrapped Network Dials**: All outbound connections route through `ssrf.DialContext`, utilized internally by `ssrf.NewHTTPClient`. This interceptor validates destinations against both blocklists and allowlists before establishing any TCP connection, preventing low-level network bypasses.

2. **Mandatory URL Validation**: Proxy providers must explicitly call `ssrf.ValidateURL` before request construction. The Vertex provider implements this check in [`proxy/providers/vertex/routing.go`](https://github.com/JuliusBrussee/caveman/blob/main/proxy/providers/vertex/routing.go) at line 62, while the Bedrock provider applies identical validation in [`proxy/providers/bedrock/routing.go`](https://github.com/JuliusBrussee/caveman/blob/main/proxy/providers/bedrock/routing.go) at line 202. This requirement ensures user-supplied URLs undergo security screening regardless of the provider type.

3. **Mode-Dependent Enforcement**: The system distinguishes between `ssrf.SelfHostedConfig` and `ssrf.ManagedConfig`. Self-hosted operators can access local services—such as a private Ollama instance—by populating the allowlist. Managed deployments ignore the allowlist entirely, guaranteeing that malicious payloads cannot forge requests to internal databases or metadata services.

## Implementation in Proxy Providers

Proxy providers integrate SSRF guards by invoking validation before HTTP client instantiation. The following pattern appears consistently across Vertex, Bedrock, and GitHubApp implementations:

```go
func makeRequest(ctx context.Context, target *url.URL) (*http.Response, error) {
    // Validate URL before any network activity
    if err := ssrf.ValidateURL(ctx, target.String(), ssrf.ManagedConfig()); err != nil {
        return nil, err
    }
    
    // Create client with enforced configuration
    client := ssrf.NewHTTPClient(ssrf.ManagedConfig())
    return client.Get(target.String())
}

```

This pattern ensures providers forwarding requests to external APIs cannot be hijacked to probe internal infrastructure. The GitHubApp provider applies the same validation in [`proxy/providers/githubapp/githubapp.go`](https://github.com/JuliusBrussee/caveman/blob/main/proxy/providers/githubapp/githubapp.go), demonstrating the universal application of these controls across all proxy implementations.

## Configuring the SSRF Allowlist

Self-hosted deployments requiring access to internal services—such as locally-hosted language models—can configure explicit exemptions:

```bash

# Permit specific internal addresses

export CAVE_SSRF_ALLOWLIST="localhost,127.0.0.1:8080,192.168.1.50"

```

In Go code, instantiate the self-hosted configuration to respect these overrides:

```go
// Initialize self-hosted config that reads CAVE_SSRF_ALLOWLIST
cfg := ssrf.SelfHostedConfig()
client := ssrf.NewHTTPClient(cfg)

// Request succeeds only if host is on allowlist
resp, err := client.Get("http://127.0.0.1:8080/health")
if err != nil {
    // Handles: ssrf: destination 127.0.0.1:8080 is in blocked range
}

```

In managed mode, identical code using `ssrf.ManagedConfig()` would reject the same request regardless of environment variables, protecting shared infrastructure from tenant isolation breaches.

## Summary

- The **SSRF allowlist** in Caveman blocks loopback, link-local, and private IP ranges by default through `ssrf.ValidateHost` in [`shared/platform/ssrf/ssrf.go`](https://github.com/JuliusBrussee/caveman/blob/main/shared/platform/ssrf/ssrf.go).
- The `CAVE_SSRF_ALLOWLIST` environment variable permits specific hosts only in **self-hosted mode**, while managed deployments ignore the allowlist to enforce strict tenant isolation.
- **Proxy providers** such as Vertex and Bedrock must call `ssrf.ValidateURL` before request construction, preventing forged requests from reaching internal networks.
- The `ssrf.DialContext` wrapper intercepts connections at the network layer, ensuring validation occurs before TCP handshakes complete.

## Frequently Asked Questions

### What is proxy-side request forgery?

Proxy-side request forgery occurs when an attacker manipulates a proxy server to make unauthorized requests to internal services or restricted networks that the attacker cannot access directly. In Caveman, this attack vector is mitigated by validating all outbound URLs against the SSRF blocklist before the proxy forwards any traffic, ensuring internal infrastructure remains unreachable.

### Why does the allowlist only work in self-hosted mode?

The allowlist is restricted to self-hosted mode (`ssrf.SelfHostedConfig`) to prevent multi-tenant managed deployments from exposing internal infrastructure to tenant workloads. This design assumes self-hosted operators understand their specific network topology and intentionally expose services like local LLM endpoints, whereas managed mode requires absolute isolation between tenant code and system networks.

### Which IP ranges are blocked by default?

Caveman's SSRF validator blocks **loopback addresses** (127.0.0.0/8 and ::1), **link-local addresses** (169.254.0.0/16), and standard **private RFC1918 ranges** (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). These restrictions are enforced in [`shared/platform/ssrf/ssrf.go`](https://github.com/JuliusBrussee/caveman/blob/main/shared/platform/ssrf/ssrf.go) and prevent unauthorized access to metadata services, local databases, and internal APIs.

### How do proxy providers enforce SSRF validation?

Proxy providers enforce validation by calling `ssrf.ValidateURL` with the appropriate configuration before instantiating HTTP clients, as demonstrated in [`proxy/providers/vertex/routing.go`](https://github.com/JuliusBrussee/caveman/blob/main/proxy/providers/vertex/routing.go) and [`proxy/providers/bedrock/routing.go`](https://github.com/JuliusBrussee/caveman/blob/main/proxy/providers/bedrock/routing.go). They subsequently use `ssrf.NewHTTPClient` with either `ssrf.ManagedConfig` or `ssrf.SelfHostedConfig`, ensuring the underlying `ssrf.DialContext` wrapper rejects forbidden destinations at the connection level.