Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Quickstart

Run iron-proxy locally with Docker Compose. By the end, you have a running proxy that allowlists specific hosts, blocks everything else, and swaps proxy tokens for real secrets. The setup uses the docker-compose example from the iron-proxy repo.

Prerequisites

  • Docker and Docker Compose
  • Git

Clone the Repo

git clone https://github.com/ironsh/iron-proxy.git
cd iron-proxy/examples/docker-compose

Start the Proxy and Demo Client

docker compose up

This starts two containers on a shared bridge network:

  • proxy: builds iron-proxy from source, generates a CA certificate on startup, and listens on 172.20.0.2
  • client: an Alpine container that uses the proxy's DNS and runs a series of demo requests

Watch the Output

The client runs five requests that demonstrate core functionality:

1. Allowed request. httpbin.org is in the allowlist, so the request succeeds:

> curl https://httpbin.org/get

2. Blocked request. example.com is not in the allowlist:

> curl https://example.com/
# Returns 403 Forbidden

3. Secret swap in Authorization header. The client sends a proxy token, and iron-proxy replaces it with the real key before forwarding:

> curl -H "Authorization: Bearer proxy-openai-abc123" https://httpbin.org/headers
# httpbin echoes back the real OPENAI_API_KEY value

4. Secret swap in custom header. INTERNAL_TOKEN is configured to match all headers:

> curl -H "X-Internal: proxy-internal-tok" https://httpbin.org/headers

5. Secret swap in query parameter. Query parameters are always scanned:

> curl "https://httpbin.org/get?token=proxy-openai-abc123&q=hello"

Check the Audit Logs

View the proxy's structured JSON logs:

docker compose logs proxy

Each request produces a log entry like:

{
  "host": "httpbin.org",
  "method": "GET",
  "path": "/headers",
  "action": "allow",
  "status_code": 200,
  "duration_ms": 142,
  "request_transforms": [
    {
      "name": "allowlist",
      "action": "continue"
    },
    {
      "name": "secrets",
      "action": "continue",
      "annotations": {
        "swapped": [
          {
            "secret": "OPENAI_API_KEY",
            "locations": ["header:Authorization"]
          }
        ]
      }
    }
  ]
}

Blocked requests include a rejected_by field and log at WARN level.

What Just Happened

Docker Setup

The Compose file creates a bridge network (172.20.0.0/24) with two services and a shared volume for the CA certificate:

services:
  proxy:
    build:
      context: ../..
      dockerfile: examples/docker-compose/Dockerfile
    environment:
      - OPENAI_API_KEY=sk-real-openai-key-do-not-share
      - INTERNAL_TOKEN=real-internal-secret-value
    volumes:
      - certs:/certs
    networks:
      demo:
        ipv4_address: 172.20.0.2
 
  client:
    image: alpine:latest
    depends_on:
      - proxy
    dns:
      - 172.20.0.2
    volumes:
      - ./client.sh:/demo/client.sh:ro
      - certs:/certs:ro
    entrypoint: ["/bin/sh", "/demo/client.sh"]
    networks:
      demo:
        ipv4_address: 172.20.0.4
 
volumes:
  certs:
 
networks:
  demo:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

The key wiring:

  • dns: [172.20.0.2] on the client points all DNS lookups at the proxy. The proxy's DNS server returns its own IP for every lookup, so all HTTP/HTTPS traffic routes through it.
  • environment on the proxy holds real secrets. The client never has access to these values.
  • certs volume is shared between both services. The proxy generates a CA cert on startup and writes it here. The client reads it so it can trust the proxy's TLS certificates.

Proxy Config

The proxy is configured via proxy.yaml:

# Built-in DNS server. Returns proxy_ip for all lookups so
# all outbound traffic routes through the proxy.
dns:
  listen: ":53"
  proxy_ip: "172.20.0.2"
 
proxy:
  http_listen: ":80"
  https_listen: ":443"
 
# CA cert used to mint leaf certificates on the fly for TLS
# interception. The client trusts this CA via the shared volume.
tls:
  ca_cert: "/certs/ca.crt"
  ca_key: "/certs/ca.key"
 
# Transforms run in order on every request.
transforms:
  # Only requests to these domains are allowed.
  # Everything else gets a 403.
  - name: allowlist
    config:
      domains:
        - "httpbin.org"
 
  # Read real secret values from the proxy container's environment.
  # Each secret maps a proxy token to an env var. When the proxy
  # sees a token in a request, it swaps in the real value before
  # forwarding upstream.
  - name: secrets
    config:
      secrets:
        - source:
            type: env
            var: OPENAI_API_KEY           # env var on the proxy container
          replace:
            proxy_value: "proxy-openai-abc123" # token the client sends
            match_headers: ["Authorization"]   # only scan this header
            match_body: false
          rules:
            - host: "httpbin.org"
 
        - source:
            type: env
            var: INTERNAL_TOKEN
          replace:
            proxy_value: "proxy-internal-tok"
            match_headers: []  # empty list = scan all headers
            match_body: false
          rules:
            - host: "httpbin.org"
 
log:
  level: "info"