An egress firewall for untrusted workloads with default-deny, secret injection, and audit logging.
10K+
linux/amd64, linux/arm64iron-proxy is an egress firewall for untrusted workloads. It sits between your container and the internet as a MITM proxy with a built-in DNS server, enforcing default-deny egress, injecting secrets so workloads never hold real credentials, and producing structured JSON audit logs for every request.
iron-proxy terminates TLS by generating leaf certificates on the fly, signed by a CA you provide. Client containers must trust this CA.
$ mkdir -p certs
$ openssl genrsa -out certs/ca.key 4096
$ openssl req -x509 -new -nodes \
-key certs/ca.key \
-sha256 -days 3650 \
-subj "/CN=iron-proxy CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign" \
-out certs/ca.crt
$ docker run -d --name iron-proxy \
-v $(pwd)/proxy.yaml:/etc/iron-proxy/proxy.yaml:ro \
-v $(pwd)/certs/ca.crt:/etc/iron-proxy/ca.crt:ro \
-v $(pwd)/certs/ca.key:/etc/iron-proxy/ca.key:ro \
-e OPENAI_API_KEY=sk-real-key \
ironsh/iron-proxy:latest -config /etc/iron-proxy/proxy.yaml
The simplest approach is DNS-based routing — point the container's DNS at iron-proxy and all hostname lookups resolve to the proxy IP, routing traffic through it automatically:
$ docker run --rm \
--dns 172.20.0.2 \
-v $(pwd)/certs/ca.crt:/usr/local/share/ca-certificates/iron-proxy.crt:ro \
alpine sh -c "update-ca-certificates 2>/dev/null && curl https://httpbin.org/get"
For stronger enforcement, layer nftables rules to block non-proxy egress, or use TPROXY for kernel-level interception. See the examples/ directory for working setups of each approach.
services:
proxy:
image: ironsh/iron-proxy:latest
command: ["-config", "/etc/iron-proxy/proxy.yaml"]
environment:
- OPENAI_API_KEY=sk-real-openai-key-do-not-share
volumes:
- ./proxy.yaml:/etc/iron-proxy/proxy.yaml:ro
- ./certs/ca.crt:/etc/iron-proxy/ca.crt:ro
- ./certs/ca.key:/etc/iron-proxy/ca.key:ro
networks:
demo:
ipv4_address: 172.20.0.2
client:
image: alpine:latest
dns:
- 172.20.0.2
volumes:
- ./certs/ca.crt:/usr/local/share/ca-certificates/iron-proxy.crt:ro
networks:
demo:
ipv4_address: 172.20.0.4
networks:
demo:
ipam:
config:
- subnet: 172.20.0.0/24
A full working demo with allowed, blocked, and secret-swap scenarios is available in the examples/docker-compose directory.
iron-proxy takes a single YAML config file. See iron-proxy.example.yaml for a copy-pasteable starting point.
dns:
listen: ":53"
proxy_ip: "10.16.0.1"
passthrough:
- "*.internal.corp"
proxy:
http_listen: ":80"
https_listen: ":443"
tls:
ca_cert: "/etc/iron-proxy/ca.crt"
ca_key: "/etc/iron-proxy/ca.key"
transforms:
- name: allowlist
config:
domains:
- "api.openai.com"
- "*.anthropic.com"
- name: secrets
config:
source: env
secrets:
- var: OPENAI_API_KEY
proxy_value: "proxy-token-123"
match_headers: ["Authorization"]
hosts:
- name: "api.openai.com"
log:
level: "info"
Default-deny. Requests must match at least one domain glob or CIDR to proceed. Unmatched requests get a 403 Forbidden.
Set real secrets as environment variables on the iron-proxy container. Give the workload a proxy token instead. The secrets transform scans outbound requests and replaces proxy tokens with real values before forwarding upstream — the workload never sees the real key.
Content type
Image
Digest
sha256:1b5de5556…
Size
19.7 MB
Last updated
9 days ago
docker pull ironsh/iron-proxy