Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
iron-proxy — Un firewall di egress per carichi di lavoro non attendibili. | Kitploit
Strumenti/GitHubGitHub/paradigmxyz/iron-proxy
Sicurezza dei ContenitoriProxy Web e IntercettazioneEsfiltrazione DatiSicurezza WebSicurezza di ReteSicurezza CloudDevSecOpsSicurezza dei Database
GitHubparadigmxyz/iron-proxy

iron-proxy

Un firewall di egress per carichi di lavoro non attendibili.

Vedi Repository
61236276 giorni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
Sito web

iron-proxy

Docs Latest Release Docker Pulls

Il problema

I job CI, gli agenti di codifica AI e i container in sandbox possono effettuare richieste outbound arbitrarie. Una dipendenza compromessa, una prompt injection o un passaggio di build malintenzionato possono esfiltrare segreti, fare phone home o aprire una reverse shell. La maggior parte dei team non ha alcuna visibilità su ciò che lascia i propri workload, figurarsi un modo per fermarlo.

Cosa fa iron-proxy

iron-proxy è un proxy egress MITM con un server DNS integrato che si pone tra il tuo workload non fidato e internet. Applica il default-deny al confine di rete, così il workload può raggiungere solo i domini che autorizzi esplicitamente. I veri segreti non entrano mai nella sandbox. I workload usano token proxy e iron-proxy li sostituisce con credenziali reali in uscita, il che significa che un workload compromesso può esfiltrare solo un token che fuori dal proxy non vale nulla.

Un singolo binario. Un singolo file YAML di configurazione.

  • Egress default-deny. Ogni richiesta outbound viene bloccata a meno che la destinazione non corrisponda alla tua allowlist. Elenca i tuoi domini e CIDR, tutto il resto riceve un 403.
  • Denylist IP upstream. Anche quando un host è consentito, il proxy rifiuta di contattarlo se il suo indirizzo risolto ricade in un CIDR negato — chiudendo il gap SSRF/DNS-rebinding in cui un hostname in allowlist punta a IMDS o a loopback. Gli endpoint di metadata cloud (169.254.169.254, fd00:ec2::254 e fd20:ce::254) e il loopback sono negati per impostazione predefinita; puoi sovrascrivere tramite proxy.upstream_deny_cidrs o IRON_PROXY_UPSTREAM_DENY_CIDRS.
  • Iniezione di segreti a livello di confine. I workload inviano token proxy; iron-proxy li sostituisce con segreti reali prima che la richiesta esca. Se la sandbox viene compromessa, l'attaccante ottiene token inutili fuori dal proxy.
  • Traccia di audit per richiesta. Ogni richiesta viene registrata come JSON strutturato con il risultato completo della pipeline di trasformazione: quali segreti sono stati sostituiti, quali regole hanno fatto match, cosa è stato bloccato e perché.
  • Consapevole dello streaming. Gli upgrade WebSocket e gli Server-Sent Events sono proxati nativamente. Nessuna configurazione speciale per workload agent che mantengono connessioni di lunga durata.
  • Supporto proxy esplicito. Listener tunnel opzionale per strumenti che supportano nativamente la configurazione proxy tramite HTTP_PROXY, HTTPS_PROXY o impostazioni SOCKS5.
  • Proxy MITM PostgreSQL. Listener opzionale che autentica i client con credenziali gestite dal proxy, inietta SET ROLE sulla sessione upstream e rifiuta i tentativi del client di modificare il ruolo (SET ROLE, set_config('role', ...), blocchi DO, ecc.) tramite un'analisi dell'AST SQL. Si combina con la row-level security di PostgreSQL per garantire l'isolamento dei dati per tenant quando l'applicazione si connette come utente condiviso service-account. Richiede che PgBouncer (se usato) sia eseguito in pool_mode = session — le modalità transaction o statement pool ricollegano silenziosamente i backend tra le query e vanificherebbero la policy. Vedi docs.iron.sh per i dettagli.

Progettato per pipeline CI, GitHub Actions, agenti AI (Claude Code, Cursor, Codex) e qualsiasi ambiente in cui esegui codice di cui non ti fidi completamente.

Esfiltrazione bloccata + riscrittura dei segreti in azione:

Installazione

Le immagini Docker sono disponibili su Docker Hub e i binari precompilati per Linux/macOS (amd64/arm64) sono su GitHub Releases.

Oppure compila dal sorgente:```bash go build -o iron-proxy ./cmd/iron-proxy

root@kitploit:~
## Avvio rapido```bash
cd examples/docker-compose
docker compose up

Questo avvia iron-proxy e un client demo che invia cinque richieste attraverso il proxy. Controlla i log per vedere le richieste consentite, bloccate e riscritte con segreti:```bash docker compose logs proxy

root@kitploit:~
Ogni richiesta produce una voce di audit JSON strutturata:```json
{
  "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"] }] }
    }
  ]
}

Le richieste rifiutate includono un campo rejected_by e vengono registrate a livello WARN. Vedi Formato del log di audit per lo schema completo.

Utilizzo in produzione

1. Genera una CA

iron-proxy termina TLS generando al volo certificati foglia, firmati da una CA che fornisci tu. I container client devono considerare attendibile questa CA.```bash 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

root@kitploit:~
### 2. Crea una rete Docker

iron-proxy necessita di un IP fisso affinché i container possano puntare il loro DNS ad esso:```bash
docker network create --subnet=172.20.0.0/24 iron-proxy

3. Avvia iron-proxy

Crea un file env con i tuoi segreti (tienilo fuori dal controllo di versione):```bash echo "OPENAI_API_KEY=sk-real-key" > .env

root@kitploit:~
Il contenuto del blocco da tradurre è vuoto. Non è stato fornito alcun testo di input per questo chunk 15 di 70. Invia il contenuto da tradurre per procedere.```bash
docker run -d --name iron-proxy \
  --network iron-proxy --ip 172.20.0.2 \
  -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 \
  --env-file .env \
  ironsh/iron-proxy:latest -config /etc/iron-proxy/proxy.yaml

4. Instrada i container attraverso il proxy

L'approccio più semplice è il routing basato su DNS: punta il DNS del container su iron-proxy e tutte le risoluzioni dei nomi host puntano all'IP del proxy, instradando il traffico attraverso di esso automaticamente:```bash docker run --rm
--network iron-proxy
--dns 172.20.0.2
-v $(pwd)/certs/ca.crt:/certs/ca.crt:ro
curlimages/curl --cacert /certs/ca.crt https://httpbin.org/get

root@kitploit:~
For stronger enforcement, layer nftables rules to block non-proxy egress, or use
TPROXY for kernel-level interception. Vedi [Instradare il traffico al
proxy](#routing-traffic-to-the-proxy) per i dettagli su ciascun approccio.

## Perché iron-proxy?

|                          | iron-proxy                     | Squid                       | mitmproxy                 | Envoy                              |
| ------------------------ | ------------------------------ | --------------------------- | ------------------------- | ---------------------------------- |
| Egress con negazione predefinita | Integrato              | Richiede configurazione ACL complessa | Richiede scripting personalizzato | Richiede configurazione RBAC/filtro |
| Iniezione di segreti     | Integrato                       | No                          | No                        | No                                 |
| Log di controllo strutturati | Integrato, tracce per trasformazione | Log di accesso di base      | Basato su plugin          | Log di accesso configurabili       |
| Complessità di configurazione | Singolo binario + YAML      | Linguaggio di configurazione esteso | Scripting Python          | YAML complesso o control plane     |

iron-proxy è progettato appositamente per un unico compito: controllare e
verificare l'egress dai workload non affidabili. Squid può fare default-deny ma
richiede una configurazione ACL significativa e non ha alcun concetto di
iniezione di segreti. mitmproxy è un ottimo strumento di debug ma non è
progettato per l'applicazione in produzione. Envoy è un proxy generico che può
essere configurato per fare parte di questo, ma rappresenta molta più
complessità di quanto il problema richieda.```
Container → DNS lookup → iron-proxy IP → TLS termination → transforms → upstream
TrasformazioneCosa fa
allowlistConsente richieste verso domini/CIDR corrispondenti; rifiuta tutto il resto (403).
secrets

Configurazione

iron-proxy accetta un singolo flag: -config path/to/config.yaml. Ecco la struttura completa (vedi iron-proxy.example.yaml per un punto di partenza copiabile e incollabile):```yaml dns: listen: ":53" proxy_ip: "10.16.0.1" # IP where iron-proxy is running (required) passthrough: # Domains forwarded to OS resolver - "*.internal.corp" - "metadata.google.internal" records: # Static DNS records (highest precedence) - name: "internal.example.com" type: A value: "10.0.0.5"

proxy: http_listen: ":80" https_listen: ":443" tunnel_listen: ":8080" # Optional CONNECT/SOCKS5 listener max_request_body_bytes: 1048576 # 1 MiB (default) max_response_body_bytes: 0 # uncapped (default)

tls: ca_cert: "/etc/iron-proxy/ca.crt" # Required ca_key: "/etc/iron-proxy/ca.key" # Required cert_cache_size: 1000 # LRU cache for generated leaf certs leaf_cert_expiry_hours: 72

transforms:

  • name: allowlist config: domains: - "api.openai.com" - "*.anthropic.com" cidrs: - "10.0.0.0/8"

  • name: secrets config: secrets: - source: type: env var: OPENAI_API_KEY # Env var holding the real secret proxy_value: "proxy-token-123" # Token the sandbox sends match_headers: ["Authorization"] match_body: false require: true # Reject requests without the proxy token rules: - host: "api.openai.com"

log: level: "info" # debug, info, warn, error

root@kitploit:~
### DNS

Per impostazione predefinita, tutto viene risolto in `proxy_ip`, che è ciò che instrada il traffico attraverso il proxy. Eccezioni:

- **`passthrough`:** modelli glob inoltrati al resolver del sistema operativo (ad es.,
  `*.internal.corp`). Il traffico verso questi host bypassa completamente il proxy.
- **`records`:** record A o CNAME statici. Priorità più alta.

### Handler di retry delle risposte

Imposta `IRON_RESPONSE_RETRY_HANDLER_URL`,
`IRON_RESPONSE_RETRY_COMPLETE_URL`, `IRON_RESPONSE_RETRY_HANDLER_TOKEN`,
`IRON_RESPONSE_RETRY_HANDLER_SANDBOX_ID` e un elenco
`IRON_RESPONSE_RETRY_STATUSES` separato da virgole per abilitare i retry delle risposte autorizzati
esternamente.

L'handler di autorizzazione riceve lo schema upstream esatto, l'authority, il metodo,
il path/query, la riproducibilità, lo stato e le intestazioni della risposta, il contesto di trace e
l'identità della sandbox. Può restituire le intestazioni della richiesta più un ID del tentativo per un
singolo replay esatto. L'handler di completamento riceve quindi lo stato del replay e
le intestazioni della risposta selezionate da `IRON_RESPONSE_RETRY_COMPLETION_HEADERS`, che
per impostazione predefinita è `Payment-Receipt`.

I body delle risposte non vengono mai inviati a nessuno dei due handler, le destinazioni non possono cambiare,
e le intestazioni di connessione/framing vengono rifiutate. Le richieste che superano
`proxy.max_request_body_bytes` procedono normalmente ma vengono contrassegnate come non riproducibili;
se contestate, viene restituita la loro risposta originale. Anche i fallimenti degli handler
preservano la risposta originale. Gli URL degli handler devono usare HTTPS a meno che loopback o
`IRON_RESPONSE_RETRY_HANDLER_ALLOW_HTTP=true` sia esplicitamente configurato per una
rete interna affidabile. I reindirizzamenti vengono rifiutati e il token di retry delle risposte
deve essere configurato indipendentemente dal token del piano di controllo. Le richieste WebSocket,
gRPC e streaming a lunghezza sconosciuta bypassano la gestione del retry delle risposte.

Quando un handler attendibile risolve all'interno di `proxy.upstream_deny_cidrs`, imposta
`IRON_RESPONSE_RETRY_HANDLER_ALLOW_CIDRS` su un elenco separato da virgole dei
CIDR privati ristretti che può utilizzare. Questa eccezione si applica solo agli endpoint
authorize e complete esattamente configurati; il traffico proxy ordinario rimane
soggetto all'intera lista deny upstream. Gli intervalli pubblici, loopback, link-local e di metadata cloud
non possono essere aggiunti tramite questa impostazione.

### Allowlist

Negazione predefinita. Le richieste devono corrispondere ad almeno un glob di dominio o CIDR per procedere.
Le richieste non corrispondenti ricevono un `403 Forbidden`.

I pattern di dominio usano il glob matching: `*.example.com` corrisponde a qualsiasi sottodominio e
allo stesso `example.com`.

**Modalità Warn:** imposta `warn: true` per osservare cosa bloccherebbe l'allowlist senza
applicarla effettivamente. Le richieste che verrebbero rifiutate vengono lasciate passare ma
annotate con `"action": "warn"` nel trace di transform. Questo è utile per
distribuire nuove regole allowlist o per svolgere l'audit del traffico esistente prima
di passare all'applicazione.

### Annotate

Cattura le intestazioni delle richieste HTTP nelle annotazioni del log di audit in base a
regole host/metodo/path. È utile per arricchire i log di audit con
contesto specifico della richiesta, come gli ID di richiesta, senza modificare il core del proxy.

Ogni gruppo di annotazioni specifica le regole di corrispondenza e le intestazioni da catturare. Quando una
richiesta corrisponde a qualsiasi regola in un gruppo, i valori delle intestazioni specificati vengono scritti come
voci `header:<Name>` nelle annotazioni del trace di transform. Le richieste che non
corrispondono vengono lasciate passare invariate. Questa trasformazione non rifiuta mai le richieste.

> **Avvertenza:** I valori delle intestazioni vengono emessi in testo semplice nel log di audit.
> Solo registra le intestazioni che sono sicure da esporre, come gli ID di richiesta o le intestazioni contenenti
> token segreti del proxy. Non registrare intestazioni che contengono segreti non elaborati.```yaml
transforms:
  - name: annotate
    config:
      annotations:
        - rules:
            - host: "api.openai.com"
              methods: ["POST"]
              paths: ["/v1/*"]
          headers: ["x-request-id"]
        - rules:
            - host: "*.anthropic.com"
          headers: ["x-request-id"]

Allowlist delle intestazioni

Filtro delle intestazioni di richiesta con negazione implicita (default-deny). Qualsiasi intestazione di richiesta il cui nome canonico non è presente nell'elenco headers configurato viene rimossa prima che la richiesta prosegua verso l'upstream. Utile per bloccare tracciamento, fingerprinting o perdite accidentali di intestazioni (cookie, ID di correlazione interni, X-Forwarded-*, ecc.) che la sandbox potrebbe allegare.

Le voci vengono confrontate senza distinzione tra maiuscole e minuscole rispetto al nome canonico dell'intestazione. I pattern delimitati da /.../ (ad es. /^X-Trace-.*$/) sono espressioni regolari senza distinzione tra maiuscole e minuscole, rispecchiando la sintassi match_headers del transform secrets.

Le rules opzionali limitano l'allowlist a host/metodi/percorsi specifici. Se omesse, l'allowlist si applica a ogni richiesta che raggiunge questo transform.

Quando almeno un'intestazione viene rimossa, la traccia viene annotata con stripped_headers che elenca i nomi rimossi.

Posizionamento: metti header_allowlist dopo secrets (così le credenziali iniettate non vengono rimosse se non sono nell'allowlist; puoi elencarle) e dopo annotate (così l'annotazione legge le intestazioni originali).```yaml transforms:

  • name: header_allowlist config: headers: - "Authorization" - "Content-Type" - "User-Agent" - "Accept" - "/^X-Trace-.*$/" rules: - host: "api.openai.com"
root@kitploit:~
### Cattura del corpo

Registra il corpo della richiesta decodificata delle richieste corrispondenti e lo espone nel
record del log di audit in un gruppo `body_capture` contenente `request_body` e
`request_body_truncated`. Utile per verificare i payload che attraversano il
proxy, come i prompt che una sandbox invia a un provider LLM, senza
modificare il traffico a monte.

Host, metodi e percorsi vengono confrontati con la stessa sintassi `rules` di
`allowlist` e `secrets`. `max_request_body_bytes` limita quanto di ogni corpo
viene catturato; i corpi più grandi del limite vengono troncati al prefisso e
`request_body_truncated` viene impostato a `true`. Il limite predefinito è 16 KiB ed è
indipendente dal limite globale `proxy.max_request_body_bytes`. Questa trasformazione
è solo osservativa: non rifiuta mai una richiesta e gli errori di lettura del corpo sono
annotati sulla traccia invece di far fallire la richiesta.

In caso di cattura riuscita, la voce della trasformazione in `request_transforms` viene
annotata con `captured_bytes` e `truncated` così la traccia registra che un
corpo è stato catturato senza duplicare il corpo stesso.

I corpi delle risposte non vengono catturati. Le risposte in streaming (SSE) dovrebbero essere
bufferizzate end-to-end prima dell'inoltro, il che bloccherebbe il client.

> **Avvertenza:** I corpi catturati vengono scritti nel log di audit in testo semplice. Quando
> `secrets` viene eseguito con `match_body: true`, posiziona `body_capture` *prima* di `secrets`
> così il log di audit registra i token proxy della sandbox anziché le credenziali reali
> che `secrets` sostituisce nel corpo.```yaml
transforms:
  - name: body_capture
    config:
      max_request_body_bytes: 16384
      rules:
        - host: "api.anthropic.com"
          methods: ["POST"]
          paths: ["/v1/messages"]
        - host: "api.openai.com"
          methods: ["POST"]
          paths: ["/v1/chat/completions"]

Segreti

La sandbox non conserva mai credenziali reali. Invece:

  1. Configura iron-proxy con la vera fonte del segreto: variabili d'ambiente, un file su disco, AWS Secrets Manager, AWS Systems Manager Parameter Store, 1Password (account di servizio) o 1Password Connect.
  2. Fornisci alla sandbox un proxy token (ad es., proxy-openai-abc123).
  3. Configura la transform secrets per mappare i proxy token a quelle fonti.

iron-proxy analizza le richieste in uscita e sostituisce i proxy token con i valori reali prima di inoltrarle a monte. Puoi controllare dove cerca:

  • match_headers: elenco dei nomi di header da analizzare. Elenco vuoto = tutti gli header. I nomi letterali vengono confrontati senza distinzione tra maiuscole e minuscole, ma la capitalizzazione che scrivi viene preservata quando l'header viene inoltrato a monte. Le voci delimitate da /.../ vengono compilate come espressioni regolari senza distinzione tra maiuscole e minuscole, confrontate con i nomi canonici degli header (ad es. /^x-.*-key$/).
  • match_body: analizza il corpo della richiesta (bufferizzato fino a max_request_body_bytes).
  • match_query: analizza la query string dell'URL. Il valore predefinito è false; attiva l'opzione per gli upstream che si aspettano il segreto in un parametro della query. Le query string compaiono spesso nei log di accesso su entrambi i lati del proxy, quindi questa opzione è disattivata per impostazione predefinita.
  • match_path: analizza il percorso dell'URL. Il valore predefinito è false; attiva l'opzione per upstream come Telegram che incorporano il segreto nel percorso (ad es. /bot<TOKEN>/sendMessage). I percorsi URL compaiono spesso nei log di accesso su entrambi i lati del proxy, quindi questa opzione è disattivata per impostazione predefinita.

I parametri della query vengono sempre analizzati.

Fonti del segreto:

  • env: legge var dall'ambiente del processo proxy. Fissata all'avvio del processo — usa file se devi ruotare il valore su un proxy in esecuzione.
  • file: legge il segreto da path su disco. Il file viene riletto a ogni ricaricamento della configurazione (all'avvio e a ogni POST /v1/reload) e, quando ttl è impostato, alla scadenza della cache — quindi puoi ruotare il segreto di un proxy in esecuzione riscrivendo il file (atomicamente: scrivi-temp + rinomina) e ricaricando, senza riavviare. Il valore è il contenuto esatto del file (nessun trimming), quindi chi scrive controlla gli spazi finali. Sono supportati ttl e failure_ttl opzionali.
  • aws_sm: legge secret_id da AWS Secrets Manager. Sono supportati region, ttl e opzionali.

Ogni fonte accetta anche un json_key opzionale. Quando è impostato, il valore risolto viene analizzato come oggetto JSON e viene estratto il singolo campo stringa di primo livello a quella chiave. Usalo per estrarre un campo da un segreto JSON.

ttl controlla per quanto tempo un valore recuperato con successo viene tenuto in cache prima dell'aggiornamento (se vuoto, lo tiene in cache per sempre). failure_ttl controlla per quanto tempo un errore di recupero viene tenuto in cache prima di riprovare; il valore predefinito è 1m ed è indipendente da ttl, quindi un TTL di successo lungo non ritarda il recupero da un'interruzione temporanea del backend.

Nota: un bug in onepassword-sdk-go interrompe le build con CGO_ENABLED=0, quindi iron-proxy fissa una fork tramite una direttiva replace in go.mod finché la correzione non arriva a monte.

Giudice

La transform giudice chiama un LLM per produrre una decisione di approvazione/rifiuto per le richieste che corrispondono alle sue regole URL. Ogni voce sotto transforms: è un'istanza di giudice indipendente con la propria policy in linguaggio naturale, backend LLM, timeout, semaforo e circuit breaker. Gli operatori possono distribuire zero, una o molte istanze di giudice con prompt diversi limitati a regole diverse.```yaml

  • name: judge config: name: "github-write-guard" # required; identifies the instance in audit logs fallback: "deny" # deny (default) | skip. No "allow" fallback ships in v1. timeout: "8s" # per-call LLM timeout max_concurrent: 100 # semaphore capacity; additional calls wait circuit_breaker: consecutive_failures: 5 cooldown: "10s" rules: # uses the same matcher as allowlist/secrets - host: "api.github.com" methods: ["POST", "PATCH", "DELETE", "PUT"] provider: type: "anthropic" # "anthropic" or "openai" model: "claude-haiku-4-5-20251001" api_key_env: "ANTHROPIC_API_KEY" max_tokens: 256 prompt: | Natural-language policy describing what is allowed for requests that match the rules above. Kept short and specific.
root@kitploit:~
Invarianti:

- Il giudice può solo rifiutare. Non approva mai una richiesta che la allowlist statica
  avrebbe negato. La negazione statica vince sempre.
- Le richieste non corrispondenti vengono ignorate: nessuna chiamata LLM, nessuna annotazione di audit.
- In caso di errore LLM, timeout, circuit-breaker aperto o output del modello malformato, si
  applica il `fallback` configurato. `deny` blocca la richiesta (il default
  consigliato per la produzione). `skip` rimanda al resto della pipeline; poiché
  iron-proxy è default-deny, le richieste non corrispondenti vengono comunque bloccate.

Ordinamento della pipeline con il secrets transform:

- **Consigliato:** posizionare il giudice **prima** del secrets transform. Il provider
  LLM vede i token del proxy, mai le credenziali reali a cui il workload ha
  accesso.
- In alternativa, posizionare il giudice dopo il secrets transform gli consente di valutare l'esatta
  forma sul wire che verrà trasmessa in uscita, al costo di inviare credenziali reali al
  provider LLM. Scegli questa opzione solo se il tuo modello di minaccia accetta questo compromesso.

Provider supportati:

- **`anthropic`** (API Messages). Usa `api_key_env`, `model`, e opzionalmente
  `base_url` e `max_tokens`.
- **`openai`** (API Chat Completions). Stessi campi di cui sopra; imposta
  `type: openai`, punta `api_key_env` alla variabile d'ambiente che contiene la tua chiave
  OpenAI e scegli un modello come `gpt-5.4-nano`.

Output di audit: ogni richiesta corrispondente aggiunge campi strutturati nella trace del
transform, tra cui `judge.instance`, `judge.decision`, `judge.reason`,
`judge.duration_ms`, `judge.input_tokens`, `judge.output_tokens`,
`judge.fallback_applied` (quando un fallback scatta) e
`judge.circuit_breaker_tripped` (quando il breaker è aperto).

Crediti: grazie a Brex per il loro progetto CrabTrap (con licenza MIT), che
ha informato questo design.

## Policy MCP

iron-proxy può parlare il [trasporto HTTP Streamable di MCP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports). Quando una richiesta corrisponde a un server MCP configurato, il proxy analizza il corpo JSON-RPC, applica una allowlist di strumenti default-deny e filtra le risposte `tools/list` così che gli strumenti negati non raggiungano mai l'agente. Le risposte SSE vengono filtrate per evento, in modo che i flussi MCP di lunga durata restino attivi.

Questa è una capacità proxy di prima classe piuttosto che un transform: le risposte MCP possono essere stream SSE aperti che trasportano messaggi arbitrari avviati dal server, il che non si adatta al contratto request/response dei transform.```yaml
mcp:
  # JSON-RPC error envelope returned to the agent on policy denial.
  # Defaults: code -32001, message "blocked by iron-proxy policy".
  error:
    code: -32001
    message: "blocked by iron-proxy policy"
  servers:
    - name: github                         # appears in audit as mcp.server
      rules:                               # standard host/method/path rules
        - host: "mcp.github.com"
          paths: ["/mcp", "/mcp/*"]
      tools:
        - name: "search_repositories"      # always allowed
        - name: "create_issue"
          when:                            # all clauses must hold; otherwise deny
            - path: "owner"                # dotted path against arguments
              equals: "ironsh"
            - path: "repo"
              in: ["iron-proxy", "tunis-v2"]
        # Anything not listed is denied (default-deny).

Behavior:

  • Applicazione di tools/call. Le chiamate a strumenti che non sono nell'elenco tools del server, o i cui arguments non superano una qualsiasi clausola when, vengono respinte senza raggiungere l'upstream. Il proxy restituisce una risposta di errore JSON-RPC con codice e messaggio configurati e l'id originale della richiesta, così il client MCP vede un normale errore di protocollo anziché un errore HTTP.
  • Filtro di tools/list. Nelle risposte a tools/list, qualsiasi strumento non presente nell'allowlist viene rimosso prima di raggiungere l'agente. Funziona sia per le risposte application/json che text/event-stream; il filtraggio SSE opera per singolo evento, quindi gli heartbeat e gli altri messaggi sul flusso passano senza subire modifiche.
  • Corrispondenza degli argomenti. Ogni clausola when ha un path con notazione a punti (es. arguments.repo, ) e uno tra (qualsiasi scalare JSON), (una lista di scalari) o (una regex su valori stringa). Le clausole sono combinate tra loro con AND. Se è omesso, lo strumento è consentito incondizionatamente.

Ordine di pipeline: l'intercettore MCP viene eseguito dopo la pipeline di trasformazione, quindi allowlist continua a determinare quali host possono essere raggiunti e secrets ha già sostituito i token del proxy quando l'intercettore valuta il corpo.

Gateway MCP

mcp_gateway instrada gli host MCP esposti al client verso server upstream concreti dopo che la policy MCP ha accettato la richiesta. Questo consente agli agenti di chiamare host interni stabili mentre iron-proxy inoltra al vero upstream e inietta credenziali che non entrano mai nella sandbox.

Le rotte del gateway si applicano solo alle richieste che corrispondono a un server MCP. La policy MCP applica comunque per prima l'allowlist degli strumenti. Se la policy rifiuta un tools/call, la rotta del gateway non viene applicata e l'upstream non viene raggiunto.```yaml mcp: servers: - name: github rules: - host: "github.mcp.local" paths: ["/mcp", "/mcp/*"] tools: - name: "search_repositories"

mcp_gateway: routes: - name: github rules: - host: "github.mcp.local" paths: ["/mcp", "/mcp/*"] upstream: "https://mcp.github.com/v1" credentials: - source: type: env var: GITHUB_MCP_TOKEN inject: header: Authorization formatter: "Bearer {{ .Value }}"

root@kitploit:~
Credentials use the same secret sources as the `secrets` transform. They are required by default. Set `require: false` on a credential to skip it when unavailable. Audit logs record the route, upstream URL, and credential injection locations, but never the injected credential values.

Limitations in v1:

- Only Streamable HTTP transport is supported. The legacy HTTP+SSE transport (separate `/messages` and `/sse` endpoints) is not.
- A JSON-RPC batch with any denied entry is rejected as a whole batch; partial-batch forwarding is not supported.
- Resources and prompts are not enforced. Agents can still call `resources/list`, `resources/read`, etc. without policy filtering.

### Body limits

Transforms that inspect or forward request/response bodies (secrets body
matching, gRPC transforms) operate on buffered bodies. Two global settings
control the maximum buffer sizes:

- **`max_request_body_bytes`** (predefinito: `1048576` / 1 MiB): limita la quantità
  del corpo della richiesta bufferizzata per le transform. I dati oltre questo limite vengono
  troncati dal punto di vista della transform ma vengono comunque inoltrati all'upstream.
- **`max_response_body_bytes`** (predefinito: `0` / senza limite): limita la quantità
  del corpo della risposta bufferizzata. Imposta a `0` per bufferizzare l'intera risposta, che
  è l'impostazione predefinita corretta per la maggior parte dei workload (ad es. pacchetti npm, pesi dei modelli).

I corpi vengono bufferizzati incrementalmente man mano che le transform li leggono, e automaticamente
riavvolti tra le fasi della pipeline. Se una transform non legge il corpo, non avviene
alcun buffering e il corpo scorre intatto.

### Tunnel listener (HTTP/CONNECT/SOCKS5)

The tunnel listener accepts absolute-form HTTP proxy requests, HTTP CONNECT,
and SOCKS5 connections on a dedicated port. This is useful for tools that
natively support proxy configuration via `HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY`
environment variables or SOCKS5 settings, rather than relying on DNS-based
routing.

To enable it, set `tunnel_listen` under `proxy`:```yaml
proxy:
  tunnel_listen: ":8080"

Quando omesso, il listener del tunnel è disabilitato.

Tutti i protocolli passano attraverso la stessa pipeline di trasformazione delle normali richieste HTTP/HTTPS. Le richieste HTTP in forma assoluta sono gestite dal normale percorso del proxy HTTP. Per CONNECT e SOCKS5, il proxy valuta una richiesta CONNECT sintetica rispetto alla tua allowlist e alle secrets transforms, così le connessioni tunnel sono soggette alla stessa policy di default-deny.

Dopo l'handshake CONNECT o SOCKS5, il proxy osserva il primo byte per rilevare il protocollo interno:

  • TLS (0x16): esegue il MITM nello stesso modo del listener HTTPS, generando un certificato foglia al volo così che le trasformazioni possano ispezionare e riscrivere la richiesta.
  • HTTP semplice: gestisce la richiesta direttamente attraverso la pipeline di trasformazione.

Esempio di HTTP CONNECT:```bash curl -x http://172.20.0.2:8080
--cacert /certs/ca.crt
https://httpbin.org/get

root@kitploit:~
**Esempio di proxy HTTP semplice:**```bash
curl -x http://172.20.0.2:8080 \
  http://httpbin.org/get

Esempio di SOCKS5:```bash curl --socks5-hostname 172.20.0.2:8080
--cacert /certs/ca.crt
https://httpbin.org/get

root@kitploit:~
Puoi anche impostare le variabili d'ambiente standard così che tutti gli strumenti
vengano instradati automaticamente attraverso il tunnel:```bash
export HTTP_PROXY=http://172.20.0.2:8080
export HTTPS_PROXY=http://172.20.0.2:8080
export ALL_PROXY=socks5h://172.20.0.2:8080

L'implementazione SOCKS5 supporta solo la modalità senza autenticazione e accetta IPv4, IPv6, e tipi di indirizzo a nome di dominio.

TLS

iron-proxy genera certificati foglia al volo, firmati dalla CA che fornisci. Il container client deve considerare attendibile questa CA (aggiungerla al trust store di sistema o passarla tramite --cacert). I certificati vengono memorizzati in una cache LRU con chiave basata sul nome host SNI.

Instradare il traffico verso il proxy

Esistono tre approcci, con un livello crescente di imposizione.

Basato su DNS (semplice)

Punta il DNS del container su iron-proxy. Tutte le risoluzioni puntano all'IP del proxy, quindi il traffico HTTP/HTTPS lo attraversa naturalmente. Questo è ciò che l'esempio Docker Compose utilizza:```yaml services: client: dns: - 172.20.0.2 # iron-proxy IP

root@kitploit:~
Facile da configurare ma facile da bypassare: il workload può impostare IP hardcoded o usare il
proprio resolver DNS per saltare del tutto il proxy.

### DNS + firewall egress nftables (imposto)

Aggiungi un firewall nftables sopra il routing DNS. Il DNS continua a indirizzare il traffico
verso il proxy, ma nftables garantisce che il workload _non_ possa comunicare con nient'altro,
anche con IP hardcoded.

La directory [`examples/nftables`](https://github.com/paradigmxyz/iron-proxy/blob/HEAD/examples/nftables/) contiene una configurazione funzionante.
Il container client carica le regole del firewall all'avvio, prima di eseguire qualsiasi
traffico applicativo:

**nftables.conf** consente il traffico verso il proxy e blocca tutto il resto:```
table ip iron {
  chain output {
    type filter hook output priority 0; policy drop;

    # allow loopback
    oif lo accept

    # allow traffic to the proxy itself (DNS + HTTP/HTTPS)
    ip daddr 172.20.0.2 tcp dport { 80, 443 } accept
    ip daddr 172.20.0.2 udp dport 53 accept

    # allow established/related (return traffic)
    ct state established,related accept

    # log and drop everything else
    log prefix "iron-proxy-drop: " drop
  }
}

docker-compose.yml: l'immagine del client è costruita con nftables preinstallato. L'entrypoint carica le regole, poi esegue la demo. CAP_NET_ADMIN è richiesto per caricare le regole:```yaml services: proxy: # ... same as DNS example ... networks: demo: ipv4_address: 172.20.0.2

client: build: context: . dockerfile: Dockerfile.client # alpine + curl + nftables dns: - 172.20.0.2 cap_add: - NET_ADMIN volumes: - ./nftables.conf:/etc/nftables.conf:ro - certs:/certs:ro networks: demo: ipv4_address: 172.20.0.4

root@kitploit:~
In una configurazione di produzione caricherai le regole in un wrapper di entrypoint e poi
`exec` il processo effettivo come utente non root senza `CAP_NET_ADMIN`.

### TPROXY (proxy trasparente)

Per ambienti in cui non puoi controllare affatto il DNS del workload, il TPROXY di nftables
può reindirizzare il traffico a livello di kernel senza alcuna cooperazione da parte del
workload. Questo intercetta i pacchetti nella catena PREROUTING e li consegna
direttamente a iron-proxy:```
table ip iron {
  chain prerouting {
    type filter hook prerouting priority mangle; policy accept;

    # redirect HTTP/HTTPS to iron-proxy via TPROXY
    tcp dport 80 tproxy to 172.20.0.2:80 meta mark set 1 accept
    tcp dport 443 tproxy to 172.20.0.2:443 meta mark set 1 accept
  }

  chain output {
    type route hook output priority mangle; policy accept;

    # mark locally-originated packets for policy routing
    tcp dport { 80, 443 } meta mark set 1
  }
}

Questo richiede la configurazione di ip rule e ip route per instradare i pacchetti marcati verso un socket locale, inoltre iron-proxy deve essere associato con IP_TRANSPARENT. La configurazione è più complessa, ma offre la garanzia più forte che il traffico non possa aggirare il proxy. TPROXY opera al di sotto del DNS, quindi intercetta indirizzi IP hardcoded, resolver personalizzati e qualsiasi altra cosa il carico di lavoro possa tentare.

Esempio Docker Compose

La directory examples/docker-compose contiene una configurazione funzionante. Gli elementi chiave:

docker-compose.yml: proxy e client su una rete bridge condivisa. I veri segreti sono impostati come variabili d'ambiente solo sul container proxy:```yaml 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 dns: - 172.20.0.2 # Point DNS at the proxy volumes: - certs:/certs:ro networks: demo: ipv4_address: 172.20.0.4

root@kitploit:~
**proxy.yaml** inserisce `httpbin.org` e `icanhazip.com` nell'allowlist, scambia due segreti:```yaml
transforms:
  - name: allowlist
    config:
      domains:
        - "httpbin.org"
        - "icanhazip.com"
      cidrs:
        - "172.20.0.0/24"

  - name: secrets
    config:
      secrets:
        - source:
            type: env
            var: OPENAI_API_KEY
          replace:
            proxy_value: "proxy-openai-abc123"
            match_headers: ["Authorization"]
            match_query: true # scan the query string
          rules:
            - host: "httpbin.org"

        - source:
            type: env
            var: INTERNAL_TOKEN
          proxy_value: "proxy-internal-tok"
          match_headers: [] # scan all headers
          rules:
            - host: "httpbin.org"

Lo script client invia cinque richieste per dimostrare ciascun comportamento:```bash

1. Allowed request

curl https://httpbin.org/get

2. Blocked request (not in allowlist)

curl https://example.com/

3. Secret swap: proxy token replaced with real key in Authorization header

curl -H "Authorization: Bearer proxy-openai-abc123" https://httpbin.org/headers

4. Secret swap: proxy token in custom header

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

5. Secret swap: proxy token in query parameter

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

root@kitploit:~
## Formato del log di audit

Ogni richiesta inoltrata tramite proxy produce una voce di log JSON strutturata:```json
{
  "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"] }]
      }
    }
  ],
  "response_transforms": []
}

Le richieste rifiutate includono un campo rejected_by e registrano il log a livello WARN.

Esportazione OpenTelemetry

Gli eventi di audit possono essere esportati come record di log strutturati OpenTelemetry per l'analisi offline in backend come Axiom, ClickHouse o Logfire. Imposta OTEL_EXPORTER_OTLP_ENDPOINT per abilitare:```bash docker run -d --name iron-proxy
-e OTEL_EXPORTER_OTLP_ENDPOINT=https://logfire-us.pydantic.dev
-e OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
-e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer "
-e OTEL_SERVICE_NAME=iron-proxy
-e OTEL_RESOURCE_ATTRIBUTES="deployment.environment=staging" \

... other flags ...

ironsh/iron-proxy:latest -config /etc/iron-proxy/proxy.yaml

root@kitploit:~
Tutta la configurazione usa le variabili d'ambiente OTEL standard:

| Variabile                     | Descrizione                                             | Predefinito      |
| ------------------------------ | ------------------------------------------------------- | ---------------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | URL del collector OTLP. L'export OTEL è disattivato se non impostato. | (disattivato)       |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | `http/protobuf` o `grpc`.                              | `http/protobuf`  |
| `OTEL_EXPORTER_OTLP_HEADERS`  | Coppie `key=value` separate da virgole per le header di autenticazione. | (nessuno)           |
| `OTEL_SERVICE_NAME`            | Nome del servizio associato a tutti i record di log.                | `iron-proxy`     |
| `OTEL_RESOURCE_ATTRIBUTES`    | Attributi di risorsa `key=value` separati da virgole.        | (nessuno)           |

Quando abilitato, ogni evento di audit viene emesso come record di log OTEL insieme ai
log JSON esistenti su stderr. Il record di log trasporta lo stesso schema della voce di
audit JSON: `host`, `method`, `path`, `action`, `status_code`, `duration_ms`,
e gli array completi `request_transforms`/`response_transforms` con annotazioni.

## API di gestione

iron-proxy può opzionalmente esporre un'API HTTP autenticata per attività
operative. Attualmente espone un singolo endpoint, `POST /v1/reload`, che rilegge
la configurazione YAML dal disco e sostituisce atomicamente la pipeline di
trasformazione con una appena costruita. La pipeline in esecuzione viene conservata
se la nuova configurazione non è valida.

Il server di gestione è disabilitato per impostazione predefinita. Per abilitarlo, aggiungi un blocco `management`
alla tua configurazione:```yaml
management:
  # Bind on loopback unless you front this with a private network or auth proxy:
  # /v1/reload can rebuild the entire transform pipeline.
  listen: "127.0.0.1:9092"
  # Env var that holds the bearer token. Defaults to IRON_MANAGEMENT_API_KEY.
  api_key_env: "IRON_MANAGEMENT_API_KEY"

Solo modalità standalone — incompatibile con la modalità gestita dal control-plane.

Ricarica un proxy in esecuzione:```bash curl -X POST http://127.0.0.1:9092/v1/reload
-H "Authorization: Bearer $IRON_MANAGEMENT_API_KEY"

root@kitploit:~
## iron.sh

Hai bisogno di backend segreti Vault/KMS, di un operatore Kubernetes o di una gestione centralizzata delle policy? [iron.sh](https://iron.sh) si basa su iron-proxy con funzionalità enterprise per i team che lo eseguono su larga scala.

## Verifica delle firme di rilascio

Gli artefatti di rilascio includono un manifesto checksum firmato:

- `checksums.txt`
- `checksums.txt.asc` (firma distaccata in formato ASCII-armored)

Usa la chiave pubblica inclusa in [`public-key.asc`](https://github.com/paradigmxyz/iron-proxy/blob/HEAD/public-key.asc) per verificare:```bash
# 1) Download release artifacts for a tag
TAG=vX.Y.Z
gh release download "$TAG" --pattern "checksums.txt" --pattern "checksums.txt.asc"

# 2) Import the project signing key
gpg --import public-key.asc

# 3) Verify the signature over checksums.txt
gpg --verify checksums.txt.asc checksums.txt

Se la verifica riesce, GPG riporterà una firma valida da Matthew Slipper <[email protected]>.

Puoi facoltativamente ispezionare l'impronta della chiave importata e confermare che corrisponda alla tua fonte attendibile prima della verifica.

Per verificare un binario specifico rispetto all'elenco di checksum firmati (esempio: iron-proxy-linux-amd64):```bash shasum -a 256 iron-proxy-linux-amd64 | grep -F "$(grep -F 'iron-proxy-linux-amd64' checksums.txt | awk '{print $1}')"

root@kitploit:~
Scarica lo strumento
Esegue la scansione delle intestazioni (e opzionalmente di query, percorso o body) per individuare token proxy e sostituisce con segreti reali provenienti da variabili d'ambiente.
body_captureRegistra i body delle richieste decodificati degli host corrispondenti come campi di audit request_body. Solo osservazione; non rifiuta mai.
  • require: quando è true, le richieste a un host corrispondente che non contengono il proxy token vengono respinte con 403. Questo impedisce a un workload compromesso di aggirare il meccanismo di scambio dei segreti con credenziali alternative. Predefinito: false.
  • hosts: limita lo scambio a domini o CIDR specifici.
  • failure_ttl
  • aws_ssm: legge name da AWS Systems Manager Parameter Store. Sono supportati region, with_decryption, ttl e failure_ttl opzionali. with_decryption è impostato su true per impostazione predefinita, che è l'impostazione prevista per i parametri SecureString.
  • 1password: risolve secret_ref (un riferimento op://vault/item/[section/]field) utilizzando un token dell'account di servizio 1Password. Il token viene letto da OP_SERVICE_ACCOUNT_TOKEN. Sono supportati ttl e failure_ttl opzionali.
  • 1password_connect: risolve lo stesso op://vault/item/[section/]field secret_ref su un server 1Password Connect self-hosted. L'URL del server viene letto da OP_CONNECT_HOST e il token API da OP_CONNECT_TOKEN. Sono supportati ttl e failure_ttl opzionali.
  • labels.0
    equals
    in
    matches
    when
  • Audit. Ogni messaggio JSON-RPC osservato viene registrato in una nuova sezione mcp nella voce di audit log: nome del server, direzione (request o response), metodo, strumento, decisione (allow, deny o filtered), motivo per i rifiuti e numero di strumenti rimossi negli eventi di filtro.