Torna agli aggiornamenti
UpdatedAug 8, 2026

foxcage — Updated!

Esegui Firefox in un contenitore Podman rootless con capabilities rimosse, rete isolata e storage effimero per contenere le evasioni dalla sandbox e prevenire il compromesso dell'host.

Condividi

foxcage icon foxcage

Esegui Firefox in un container Podman rootless per l'isolamento di sicurezza. Il tuo browser gira con quasi nessuna Linux capability, nel proprio user e network namespace, isolato dall'host — pur mantenendo pieno supporto per l'accelerazione GPU, l'audio e i DRM.

Perché foxcage?

Firefox dispone già di una sandbox multi-processo che isola i renderer dei contenuti web usando Linux namespaces e seccomp-bpf. Per la maggior parte delle minacce, questo è efficace. foxcage aggiunge una seconda barriera: se un attaccante sfrutta una vulnerabilità che elude la sandbox di Firefox (cosa che accade — esistono CVE per questo), si ritrova dentro un container bloccato invece che nella tua intera sessione utente.

Cosa protegge foxcage

  • Accesso ai file post-exploit. Una sandbox escape su Firefox nudo dà accesso a tutto ciò che il tuo utente può leggere: ~/.ssh, ~/.gnupg, profili del browser per altri browser, database dei password manager, documenti, codice sorgente. In foxcage, l'attaccante vede solo ciò che hai montato esplicitamente.
  • Residui di tracciamento su disco. La gabbia effimera @tmp non lascia alcuna traccia su disco dopo la chiusura della finestra — incluse estensioni, stato HSTS, cache delle sessioni TLS e cache DNS che la Navigazione privata di Firefox continua a conservare. Più gabbie @tmp possono essere eseguite contemporaneamente senza interferire tra loro.
  • Persistenza. Su Firefox nudo, i malware possono scrivere in ~/.config/autostart, ~/.bashrc, cron o ovunque altro per sopravvivere a un riavvio. Il container effimero di foxcage (--rm) significa che nulla persiste a meno che tu non lo abbia montato con bind.
  • Movimento laterale di rete. Di default, il container non può sondare i servizi su localhost. Su Firefox nudo, una sandbox escape ha accesso completo alla rete. (Usa [network] mode = "host" se una gabbia necessita di accesso a localhost, ad esempio per lo sviluppo locale — ma vedi l'avvertenza nella sezione "Rete": la modalità host espone anche i socket Unix astratti dell'host.)
  • Escalation dei privilegi. Il container elimina tutte le Linux capabilities tranne CAP_SYS_CHROOT e blocca l'acquisizione di nuovi privilegi. I binari setuid, gli exploit del kernel tramite syscall oscure e percorsi di escalation simili sono tagliati fuori.

Cosa foxcage non protegge

  • Attacchi a livello di browser. Phishing, estensioni dannose e qualsiasi cosa operi all'interno della normale funzionalità di Firefox non è influenzata — foxcage isola il container dall'host, non l'utente dal browser.
  • Directory montate con bind. Qualunque cosa tu monti (profile, downloads_dir, bind mount aggiuntivi) è completamente accessibile a un browser compromesso. Se monti una directory di profilo dell'host, un attaccante può manometterla proprio come su Firefox nudo.
  • Cattura audio tramite PulseAudio. Il socket PulseAudio è montato con bind nel container. Sebbene sia montato in sola lettura a livello di filesystem, i socket Unix sono bidirezionali — un processo compromesso può comunque inviare richieste di registrazione attraverso il socket. Una sandbox escape del browser potrebbe potenzialmente registrare l'audio dal microfono dell'host.
  • Exploit del compositor Wayland. Il socket Wayland viene passato attraverso. I compositor Wayland isolano i client l'uno dall'altro per progettazione, ma una vulnerabilità nel compositor stesso sarebbe raggiungibile.

Configurazione di sicurezza

Il container viene eseguito con:

  • Tutte le Linux capabilities rimosse (solo CAP_SYS_CHROOT aggiunta di nuovo per la sandbox dei contenuti di Firefox; CAP_SETUID/CAP_SETGID aggiunte temporaneamente quando init.root è configurato)
  • no-new-privileges per prevenire l'escalation dei privilegi
  • Rootless user namespace (--userns keep-id)
  • /dev/shm privato (non condiviso con l'host) — dimensione configurabile tramite shm_size
  • Rete isolata tramite pasta con loopback dell'host bloccato di default
  • Il DNS usa il DNS dell'host di default (configurabile tramite network.dns)
  • Solo socket specifici da XDG_RUNTIME_DIR vengono montati con bind (Wayland, PulseAudio, PipeWire e il proxy D-Bus filtrato) — l'intera directory runtime dell'host non viene mai esposta
  • L'accesso al bus di sessione D-Bus dell'host è sempre mediato da un xdg-dbus-proxy filtrato in esecuzione sull'host. Solo org.freedesktop.Notifications, org.freedesktop.portal.Desktop, org.mozilla.*, e (per i fork) il namespace proprio del fork (ad es. org.librewolf.*) sono raggiungibili — i servizi di sessione come il portachiavi e l'agente SSH/GPG sono bloccati
  • L'accesso ai portal è ampio. org.freedesktop.portal.Desktop è permesso nel suo insieme, perché è così che funzionano il selettore di file, "apri link in un'altra app" e la condivisione dello schermo. Espone anche RemoteDesktop (tastiera/mouse sintetici per l'intera sessione), Camera e Location. Questi sono controllati dai dialoghi di approvazione del tuo desktop, non da foxcage — e il prompt di RemoteDesktop somiglia a quello della condivisione dello schermo, quindi leggi i dialoghi di approvazione prima di accettarli. xdg-dbus-proxy non ha una regola "nega un'interfaccia", quindi restringere questo significa elencare ogni interfaccia di cui Firefox ha bisogno; vedi docs/DESIGN.md per il motivo per cui non viene fatto di default
  • Tutti i bind mount (profile, downloads_dir, [mounts] bind aggiuntivi) usano nosuid,noexec
  • Download del browser verificato tramite firme GPG: Firefox contro i checksum SHA-512 firmati da Mozilla, LibreWolf contro la firma distaccata dei manutentori di LibreWolf più il relativo SHA-256
  • Container effimero (--rm) — le scritture sul filesystem vanno perse all'uscita
  • Nessun dispositivo host (webcam, chiavi di sicurezza, stampanti) passato attraverso se non esplicitamente abilitato

Ogni opzione [network] e [mounts] che abiliti scambia un po' di isolamento per comodità. I valori predefiniti sono la configurazione più restrittiva che ti offre comunque un browser utilizzabile.

Requisiti

  • Python 3.11+
  • Podman (rootless)
  • Wayland compositor (X11 non è supportato)
  • pasta (sudo apt install passt) — a meno che network.mode = "host"
  • xdg-dbus-proxy (sudo apt install xdg-dbus-proxy)
  • PulseAudio o PipeWire con compatibilità PulseAudio (per l'audio)
  • GPU con supporto DRI — opzionale; senza /dev/dri, foxcage avvisa e Firefox esegue il rendering via software

Esegui foxcage come tuo normale utente desktop, non come root o tramite sudo — la sandbox mappa il tuo utente nel container, e l'esecuzione come root rimuove l'isolamento che foxcage esiste per fornire. Si rifiuta di avviarsi come root.

Ambiente testato: Debian 13 (Trixie) con GNOME 3. Altre distribuzioni Linux e compositor Wayland potrebbero funzionare ma non sono state testate.

Installazione

foxcage è un singolo script Python senza dipendenze al di fuori della libreria standard di Python. Copialo in una directory nel tuo PATH:```sh sudo cp foxcage /usr/local/bin/foxcage

Oppure per un'installazione a livello utente:```sh
cp foxcage ~/.local/bin/foxcage

Assicurati che lo script sia eseguibile (chmod +x foxcage).

Controlla quale revisione hai con foxcage --version — utile quando segnali un problema, dato che foxcage viene installato copiando un singolo file.

Utilizzo```sh

./foxcage

Al primo avvio lo script crea l'immagine del container (scarica Firefox da Mozilla, installa le dipendenze minime di Debian) e poi avvia Firefox. Alle esecuzioni successive, foxcage verifica la presenza di aggiornamenti di Firefox e ricostruisce automaticamente l'immagine quando è disponibile una nuova versione. L'immagine viene anche ricostruita periodicamente (ogni 7 giorni per impostazione predefinita) per recepire gli aggiornamenti dei pacchetti di sistema. Se il controllo degli aggiornamenti fallisce (errore di rete, timeout), viene registrato un avviso e viene usata l'immagine esistente — l'avvio non viene mai bloccato.

Passa gli argomenti a Firefox:```sh
./foxcage https://example.com

Combina una gabbia denominata con i flag di Firefox:```sh ./foxcage @work --kiosk https://example.com

Se una cage è già in esecuzione, l'URL si apre in una nuova scheda nel browser esistente invece di avviare un secondo container. Eseguire `foxcage` (o `foxcage @cage`) senza URL contro una cage in esecuzione termina senza errori con un messaggio "cage is already running" — foxcage non può sollevare una finestra Wayland esistente dall'esterno del container, quindi non ci prova.

I flag di avvio **non** si applicano quando una cage è già in esecuzione. `--dns`, `--ipv4-only`, `--lifetime`, `--color` e `--fork` vengono consumati quando il container viene avviato, e le impostazioni di un container in esecuzione non possono essere modificate dall'esterno, quindi vengono ignorati con un avviso. Chiudi la cage e riesegui per applicarli.

> Usa la chiave di configurazione `private_browsing` per le sessioni in modalità privata — *non* il flag CLI grezzo `--private-window` di Firefox. La chiave di configurazione imposta la modalità privata a livello di sessione (`browser.privatebrowsing.autostart`), così le successive invocazioni `foxcage @cage URL` possono riaprire nelle schede. `--private-window` come pass-through di Firefox renderebbe privata solo la prima finestra e romperebbe il comportamento di riapertura nelle schede descritto sopra.
>
> **Attenzione:** le sessioni abilitate tramite `private_browsing = true` non mostrano i normali indicatori UI della finestra privata di Firefox (barra di accento viola, icona della maschera, "(Private Browsing)" nel titolo). Questo perché ogni finestra della sessione è privata, quindi Firefox non ha una finestra non privata con cui creare un contrasto visivo — e sopprime l'indicatore. La sessione *è* realmente privata; verifica se vuoi visitando `about:privatebrowsing` nella cage (mostra la pagina informativa standard sulla navigazione privata) oppure `about:config` e controllando `browser.privatebrowsing.autostart = true`.

### Navigazione effimera con `@tmp`

Per link monouso che non devono lasciare traccia, usa la cage riservata `tmp`:```sh
./foxcage @tmp https://somewhere-suspicious.example

Ogni avvio @tmp è un Firefox nuovo e usa e getta, senza profilo persistente. Quando la finestra si chiude, tutto sparisce — cookie, cache, cronologia, estensioni, stato HSTS, cache di sessione TLS, cache DNS, stato delle schede salvato. Questo va oltre la Navigazione privata di Firefox, che conserva ancora le estensioni e una buona parte dello stato su disco.

Più gabbie @tmp vengono eseguite in contemporanea, ciascuna isolata dalle altre. La barra dei menu mostra FoxCage - tmp (<short id>) così puoi distinguere le finestre effimere simultanee.

Le gabbie effimere aprono una pagina vuota all'avvio e nuove schede vuote — la home page predefinita di Firefox e i contenuti delle nuove schede (siti principali, consigli di Pocket, activity stream) sono puro rumore su un profilo nuovo che sta per essere scartato, quindi vengono soppressi. Le gabbie persistenti mantengono le impostazioni predefinite di Firefox.

Gabbie effimere denominate

Se vuoi un nome significativo per una sessione usa e getta (ad esempio, una tana del coniglio di ricerca che vorrai riaprire in una nuova scheda), usa @tmp-<name>:```sh ./foxcage @tmp-research https://example.com # first call → new window ./foxcage @tmp-research https://another.example # second call → new tab in the existing window

`@tmp-<name>` è ancora effimero — quando chiudi la finestra, sparisce tutto. La differenza rispetto al semplice `@tmp` è che gli avvii successivi con lo stesso nome **riutilizzano la finestra esistente** (come le gabbie persistenti), quindi puoi aggiungere altre schede in seguito senza avviare una copia parallela. Il semplice `@tmp` mantiene il suo comportamento "ogni avvio è un nuovo usa e getta".

L'etichetta della barra dei menu mostra il nome che hai scelto (`FoxCage - tmp-research`), quindi la finestra è etichettata in modo significativo.

#### Personalizzare le impostazioni predefinite effimere

Crea `~/.config/foxcage/tmp.toml` per impostare i valori predefiniti per tutte le gabbie effimere (sia il semplice `@tmp` che ogni `@tmp-<name>`). Ad esempio:```toml
private_browsing = true
lifetime = "30m"

[network]
dns = "cloudflare"

Ogni avvio effimero ora riceve una finestra privata, Cloudflare DoH e si chiude automaticamente dopo 30 minuti, con piena effimeralità intatta. Gli effimeri nominati ereditano tmp.toml per impostazione predefinita; se vuoi sovrascrivere per nome, crea ~/.config/foxcage/tmp-<name>.toml. Quel file viene quindi applicato al posto di tmp.toml — nessuna fusione, il file più specifico vince direttamente. Copia le impostazioni predefinite condivise al suo interno se le vuoi.

Tutto ciò che puoi impostare nella configurazione di una normale cage funziona qui, tranne la chiave che sconfiggerebbe l'effimeralità stessa:

  • profile — errore bloccante.

Punta a una directory del profilo persistente sull'host, che contraddice direttamente lo scopo di @tmp. Se vuoi una cage in sandbox con un profilo persistente, usa una normale cage nominata (@work, @research, ecc.) che non inizi con tmp-.

Sovrascrivere il DNS per avvio

Il flag --dns (e l'equivalente chiave di configurazione network.dns) accetta tre forme:```sh ./foxcage @tmp --dns 1.1.1.1 https://example.com # IP ./foxcage @tmp --dns cloudflare https://example.com # alias ./foxcage @tmp --dns https://dns.nextdns.io/ # custom DoH URI

**Quando il valore corrisponde a un provider noto (per alias o per IP), foxcage abilita automaticamente il DNS over HTTPS forzato verso quel provider.** Il TRR di Firefox viene impostato sulla modalità 3 (strict, nessun fallback in chiaro) con l'indirizzo bootstrap compilato, così non c'è alcuna fuga di risoluzione non crittografata all'avvio. Vedi una notifica di una riga su stderr come `Enabling DNS over HTTPS via Cloudflare`.

Alias integrati:

| Alias | IP | Filtraggio |
|-------|------|-----------|
| `cloudflare` | 1.1.1.1 | nessuno |
| `cloudflare-security` | 1.1.1.2 | blocca il malware |
| `cloudflare-family` | 1.1.1.3 | blocca malware e contenuti per adulti |
| `google` | 8.8.8.8 | nessuno |
| `quad9` | 9.9.9.9 | blocca il malware (predefinito Quad9) |
| `quad9-unfiltered` | 9.9.9.10 | nessuno |
| `adguard` | 94.140.14.14 | blocca pubblicità e tracker |
| `adguard-family` | 94.140.14.15 | pubblicità, tracker e contenuti per adulti |
| `opendns` | 208.67.222.222 | parziale |

Un IP che non è nella tabella (ad es. il Pi-hole della tua LAN) rimane solo in chiaro — nessun DoH viene abilitato, poiché foxcage non conosce l'endpoint DoH corrispondente. Usa la forma URI in quel caso: `--dns https://pi.hole/dns-query` (con un certificato valido) abilita DoH e lascia invariato il DNS del container.

La forma URI salta l'impostazione del DNS in chiaro del container, quindi tutto ciò che si trova nel container e non è Firefox continua a usare il DNS dell'host. È una scelta deliberata — `--dns URI` significa "fai usare a Firefox questo resolver DoH", punto e basta.

`--dns` è incompatibile con `network.mode = "host"`, che ha già pieno accesso alla rete dell'host.

### Identificazione visiva delle gabbie

Ogni gabbia nominata riceve un colore di accento nella barra dei menu, così puoi distinguere le finestre a colpo d'occhio. **Non devi configurare nulla** — il colore è derivato deterministicamente dal nome della gabbia (hash SHA256 trasformato in tonalità, con saturazione e luminosità fisse). `@banking`, `@work`, `@personal`, `@tmp-research` ottengono tutti colori distinti e stabili senza che tu muova un dito.

La gabbia predefinita (anonima) mantiene l'arancione incorporato.

Se vuoi sovrascrivere il colore derivato automaticamente, impostalo esplicitamente:```toml
# ~/.config/foxcage/banking.toml
color = "#dc2626"   # red — overrides the auto-derived colour

Il contenuto da tradurre non è stato incluso: dopo "INPUT:" non c'è testo. Non è possibile fornire una traduzione senza il contenuto effettivo della chunk 21/73.```sh ./foxcage @experiment --color "#10b981" https://example.com # teal, one-off

Accetta codice CSS hex standard: `#rgb`, `#rrggbb` o `#rrggbbaa` (con alfa). I colori derivati automaticamente sono calibrati per essere visibili sia su barre dei menu chiare che scure (luminosità fissata al 55%, saturazione al 75%), quindi non dovresti aver bisogno di sovrascriverli per motivi di tema.

### Gabbie con limiti di tempo

Il flag `--lifetime` (e la corrispondente chiave di configurazione `lifetime`) chiude automaticamente una gabbia dopo una durata prestabilita. Il formato è `<number><unit>` con unità `s`, `m` o `h`:```sh
./foxcage @tmp --lifetime 10m https://example.com
./foxcage @work --lifetime 2h

Il conto alla rovescia inizia quando Firefox viene effettivamente avviato nella gabbia — il tempo di avvio del container e di creazione dell'immagine non intacca il tuo budget. L'etichetta della barra dei menu della gabbia mostra il conto alla rovescia insieme all'identità della gabbia, ad es. FoxCage - tmp (a3f2b1) | 9m — aggiornata una volta al minuto quando manca più di un minuto, e una volta al secondo nell'ultimo minuto. Quando il conto alla rovescia arriva a zero, Firefox si chiude da solo e il container termina. Se chiudi Firefox manualmente prima che la durata scada, non succede nulla di insolito.

Imposta una durata predefinita per ogni gabbia nella sua configurazione:```toml

~/.config/foxcage/tmp.toml — every @tmp launch auto-closes after 15 minutes

lifetime = "15m" private_browsing = true

`--lifetime` sulla riga di comando ha la precedenza su qualsiasi valore di configurazione.

Forza una ricostruzione completa dell'immagine (scarica nuovamente Firefox e tutti i pacchetti di sistema):```sh
./foxcage --rebuild

Un container in esecuzione mantiene l'immagine da cui è stato avviato, anche dopo che foxcage ricostruisce il tag dell'immagine. Se provi ad aprire una scheda in una cage la cui immagine è stata nel frattempo aggiornata (tramite --rebuild, un aggiornamento di Firefox o la ricostruzione pianificata), foxcage rifiuta con un errore (mostrato anche come notifica desktop) e ti chiede di uscire da Firefox e rilanciarlo — operazione che avvia un nuovo container sull'immagine corrente. Con --rebuild su una cage attiva, foxcage avvisa in anticipo, esegue la build e poi applica lo stesso controllo.

Aggiornamenti

foxcage controlla la disponibilità di nuove versioni del browser a ogni avvio — l'API delle release di Mozilla per Firefox, l'endpoint delle release di GitLab per LibreWolf. Se è disponibile un aggiornamento, l'immagine del container viene ricostruita automaticamente. L'immagine viene inoltre ricostruita periodicamente (ogni 7 giorni per impostazione predefinita) per ricevere gli aggiornamenti di sicurezza di Debian. L'auto-aggiornamento integrato del browser è disabilitato poiché gli aggiornamenti sono gestiti a livello di immagine.

Se il controllo degli aggiornamenti fallisce (nessuna rete, timeout dell'API), viene stampato un avviso e viene usata l'immagine esistente — puoi sempre navigare.

La cadenza degli aggiornamenti si trova al livello superiore della configurazione; il pinning di versione e canale si trova nella sezione per-fork:```toml rebuild_days = 14 # rebuild for base-image updates every 14 days (0 to disable)

[firefox] channel = "beta" # track the beta channel instead of stable (firefox only) version = "149" # pin to Firefox 149.x (latest patch release)

**Per fissare una versione ESR serve anche il canale.** L'indice delle versioni di Mozilla elenca le release ESR senza il suffisso `esr` che i loro download portano, quindi un semplice `version = "140"` sul canale predefinito risolve a una release che non esiste. Imposta entrambi:```toml
[firefox]
channel = "esr"
version = "140"        # → 140.13.0esr

Un pin che non corrisponde ad alcuna release ora è un errore che indica il pin, invece di ripiegare silenziosamente sull'ultima release. Un errore temporaneo nel raggiungere l'API di Mozilla emette ancora un avviso e prosegue con l'immagine esistente, quindi una rete instabile non blocca mai l'avvio.

I pin con suffisso devono essere completamente qualificati — "140.13.0esr" e "150.0b9" funzionano, "140esr" e "150b9" vengono respinti al caricamento della configurazione perché nessuna release può mai corrispondervi. Lo stesso vale per le revisioni di LibreWolf: "146.0.1-1" funziona, "146-1" no.

Per forzare una ricostruzione completa immediata: ./foxcage --rebuild

Fork di Firefox (LibreWolf)

foxcage può eseguire un fork di Firefox orientato alla privacy al posto del Firefox upstream:```toml fork = "librewolf" # default is "firefox"

[librewolf] version = "146.0.1-1" # optional pin; partial pins ("146", "146.0.1") also work

Oppure per avvio tramite CLI:```sh
foxcage @tmp --fork librewolf https://example.com

LibreWolf: fork di Firefox con privacy rafforzata — protezione rigorosa dal tracciamento, DoH, RFP, telemetria disattivata per impostazione predefinita. Tarball Linux firmato da GitLab (librewolf-community/browser/bsys6), verificato via GPG contro la chiave dei LibreWolf Maintainers 662E 3CDD 6FE3 2900 2D0C A5BB 4033 9DD8 2B12 EF16 con un controllo incrociato tramite il file .sha256sum associato. Il librewolf.cfg incluso in LibreWolf viene preservato; foxcage aggiunge le proprie preferenze sopra, invece di sovrascriverle.

Il canale è solo per Firefox: firefox.channel = "beta" | "esr" viene rifiutato quando fork è diverso da "firefox". LibreWolf ha un unico canale di rilascio.

Il cambio di fork (tramite config o --fork) modifica l'hash del Containerfile, che attiva una ricostruzione al prossimo avvio — non serve --rebuild manuale.

Compatibilità dei profili

Usa un profilo dedicato per ogni fork. L'impostazione predefinita più sicura è lasciare che foxcage fornisca un proprio profilo (ometti profile dalla config), oppure punta profile a una directory che non apri anche dall'host.

  • LibreWolf: di solito è sicuro condividerlo con il profilo Firefox dell'host — LibreWolf segue le versioni di Firefox entro pochi giorni, quindi i conflitti di schema di compatibility.ini sono rari. Rischi: (1) è sicuro solo l'uso sequenziale (il file di lock di Firefox impedisce aperture simultanee); (2) nella breve finestra dopo una release stabile di Firefox, eseguire prima Firefox e poi LibreWolf può attivare un dialogo di migrazione "usato da una versione più recente"; (3) le funzionalità che LibreWolf rimuove (Sync, Pocket, account Mozilla) non funzionano silenziosamente ma non corrompono i dati.

Gabbie nominate

Esegui istanze sandbox separate con la propria config e il proprio profilo Firefox:```sh ./foxcage @work

Carica `~/.config/foxcage/work.toml` e usa un'immagine separata (`foxcage-work`), un container (`foxcage-work`) e un volume (`foxcage-work-profile`). Il file di configurazione deve esistere per le gabbie nominate. I nomi delle gabbie possono contenere solo lettere, cifre, trattini e trattini bassi.

## Configurazione

I file di configurazione si trovano in `$XDG_CONFIG_HOME/foxcage/` (predefinito: `~/.config/foxcage/`).

- `config.toml` — gabbia predefinita (opzionale, con valori predefiniti sensati senza di essa)
- `<name>.toml` — gabbia nominata, caricata con `@<name>` (obbligatoria)

Le chiavi di configurazione sconosciute vengono respinte con un errore. Vedi `config.toml.example` per tutte le opzioni disponibili con i valori predefiniti.

### Esempio di config.toml```toml
# Bind-mount a host Firefox profile directory into the cage
profile       = "~/.mozilla/firefox/xxxxxxxx.default-release"

# Allow downloading files to ~/Downloads
downloads_dir = "~/Downloads"

# Shared memory size for Firefox IPC (default: 256m)
# shm_size = "256m"

# Pass through webcam devices (/dev/video*)
# webcam = true

# Pass through host CUPS socket for locally-connected printers (e.g. USB)
# local_printers = true

# Pass through FIDO2/U2F security key devices (/dev/hidraw*)
# security_keys = true

# Always open Firefox in private browsing mode
# private_browsing = true

# Auto-close the cage after a duration (<int> with unit s, m, or h)
# lifetime = "30m"

# Accent colour for the menu-bar label.  Named cages get a colour derived
# from the name automatically; set this to override it.
# color = "#4a90e2"

# Browser fork: "firefox" (default) or "librewolf"
# fork = "librewolf"

# Full image rebuild interval in days for base-image updates (default: 7, 0 to disable)
# rebuild_days = 7

[firefox]
# Firefox release channel: "release" (default), "beta", "esr".
# Only valid when fork = "firefox".
# channel = "release"

# Pin to a specific Firefox version (overrides channel).
# Partial versions like "149" or "149.0" resolve to the latest patch release.
# Suffixed versions must be fully qualified ("140.13.0esr", "150.0b9"); to
# follow the ESR line by major version, pair a numeric pin with
# channel = "esr" above.
# version = "149.0.2"

[librewolf]
# Pin to a specific LibreWolf version. Tags are "<firefox-version>-<rev>",
# e.g. "146.0.1-1". Partial pins like "146" or "146.0.1" also work.
# version = "146.0.1-1"

[network]
# "host" for full host networking (needed if the cage has to reach services
# on the host's localhost), or omit for isolated pasta (default)
# mode = "host"

# DNS server (isolated mode only, default: host DNS)
# dns = "1.1.1.1"

# Disable IPv6 in the cage (isolated mode only)
# ipv4_only = true

[mounts]
# Additional bind mounts into the container. Supported forms:
#   "~/Documents"                     — same path in container
#   "~/Documents:~/Documents"         — ~ expanded on both sides
#   "~/Documents:/home/user/Documents" — explicit container path
# Append :ro for read-only, e.g. "~/Documents:ro"
# nosuid,noexec are always enforced on bind mounts; an explicit "exec" or
# "suid" is rejected rather than silently dropped.
# Host paths must be absolute or start with "~/".
bind = [
    "~/Documents:ro",
]

[init]
# Commands to run at image build time (as root). Changes trigger a rebuild.
# build = ["apt-get update && apt-get install -y --no-install-recommends vim"]

# Commands to run at container startup as root, before Firefox.
# root = ["chown user:user /some/path"]

# Commands to run at container startup as your user, before Firefox.
# user = ["mkdir -p ~/custom-dir"]

Profilo Firefox dell'host

Per condividere un profilo Firefox dell'host con la cage, imposta profile sulla directory del profilo. Trova il percorso del tuo profilo visitando about:profiles in Firefox sull'host — oppure punta a una nuova directory vuota se vuoi che la cage inizi con un profilo pulito che persiste sull'host.```toml profile = "~/.mozilla/firefox/xxxxxxxx.default-release"

Solo questa singola directory è montata tramite bind mount nella gabbia. I profili fratelli sotto `~/.mozilla/firefox/` e il registro `profiles.ini` non sono esposti — una gabbia compromessa non può manometterli.

Se `profile` non è impostato, un volume Podman denominato memorizza invece il profilo Firefox (vedi "Cosa persiste" sotto). Se lo stesso profilo è già aperto in Firefox sull'host, il file di blocco per profilo di Firefox causerà un conflitto — usa un profilo dedicato per ogni gabbia.

### Rete

Per impostazione predefinita, il contenitore usa pasta con loopback host bloccato e DNS host. pasta richiede podman 4.4 o versioni successive (è l'opzione predefinita rootless da podman 5.0).

**Rete host** rimuove completamente l'isolamento di rete. Usa questa modalità quando la gabbia deve raggiungere servizi sul `localhost` dell'host (ad es. un server di sviluppo locale, un database su `127.0.0.1`):```toml
[network]
mode = "host"

dns cannot be combined with mode = "host" — host networking already uses the host's resolver.

La modalità host rinuncia a più del solo localhost. Mette la gabbia nel network namespace dell'host, e i socket Unix astratti sono limitati a quel namespace piuttosto che al filesystem. Quindi una gabbia in modalità host può raggiungere direttamente i socket con indirizzo astratto sull'host — inclusi @/tmp/.X11-unix/X0 di Xwayland se esegui X11 o Xwayland (logging degli input, nonostante foxcage supporti solo Wayland), e un bus di sessione configurato con unix:abstract=…, che aggirerebbe il proxy D-Bus filtrato. Questo è intrinseco alla condivisione dello stack di rete, non qualcosa che foxcage possa filtrare. Usa la modalità host quando ti serve, e preferisci una gabbia nominata che avvii solo per quello scopo.

Le gabbie solo IPv4 disabilitano completamente IPv6:```toml [network] ipv4_only = true

Oppure, per ogni avvio, con il flag `--ipv4-only` (forma breve `-4`, come in `ssh`/`curl`/pasta):```sh
./foxcage @tmp -4 https://example.com

Questo esegue pasta in modalità solo IPv4 (-4), quindi il container non ha alcuno stack IPv6, e imposta inoltre network.dns.disableIPv6 in Firefox così da non risolvere i record AAAA — il che è rilevante quando DoH è abilitato, poiché le risposte DoH aggirano il resolver del container. ipv4_only non può essere combinato con mode = "host" — la rete host usa direttamente lo stack di rete dell'host, quindi disabilita invece IPv6 sull'host.

Comandi di init

Esegui comandi personalizzati in fase di build o all'avvio del container tramite [init]:

  • build — viene eseguito in fase di build dell'immagine come root. Usalo per installare pacchetti o altre operazioni di setup lente. Le modifiche ai comandi di build innescano automaticamente una ricostruzione dell'immagine.
  • root — viene eseguito all'avvio del container come root, prima di Firefox. Usalo per attività root rapide a runtime (regolazione dei permessi, scrittura di file di configurazione).
  • user — viene eseguito all'avvio del container come tuo utente, prima di Firefox. Usalo per creare directory e impostare lo stato a livello utente.```toml [init] build = [ "apt-get update && apt-get install -y --no-install-recommends fonts-noto-cjk", "rm -rf /var/lib/apt/lists/*", ] root = ["chmod 777 /tmp/shared"] user = ["mkdir -p ~/workspace"]
Tutte e tre le chiavi sono elenchi di stringhe di comandi shell. Se un comando fallisce, il container esce senza avviare Firefox.

**Nota di sicurezza:** Quando `init.root` è impostato, il container si avvia come root con `CAP_SETUID` e `CAP_SETGID` aggiunti (oltre al `CAP_SYS_CHROOT` predefinito) così da poter tornare all'utente regolare. Queste capacità sono mantenute solo durante la fase di init come root — dopo l'abbassamento dei privilegi, il processo dell'utente regolare non ha capacità aggiuntive. Senza `init.root`, il container viene eseguito con il set di capacità minimo predefinito.

## Cosa persiste

Senza configurazione, un volume Podman nominato memorizza il profilo Firefox (segnalibri, impostazioni, estensioni, plugin Widevine DRM). Tutto il resto è effimero.

- Cage predefinita: `foxcage-profile`
- Cage nominata: `foxcage-<name>-profile`

Per ricominciare da zero, rimuovi il volume:```sh
podman volume rm foxcage-profile

Se profile è impostato, la directory host viene montata direttamente in bind e non viene creato alcun volume.

Utilizzo del disco

Ogni immagine cage è di circa 1 GB. Una ricostruzione riapplica il tag all'immagine e lascia la precedente come voce <none> senza tag, quindi foxcage rimuove l'immagine appena sostituita dopo ogni build riuscita. Rimuove solo quella specifica immagine, mai quella ancora in uso da una cage in esecuzione.

Le immagini orfane create prima dell'introduzione di questo comportamento non vengono ripulite retroattivamente. Per recuperarle:```sh podman images --filter dangling=true # review first podman image prune # then remove

Gli aggiornamenti di Firefox vengono rilevati automaticamente a ogni avvio. Per forzare una ricostruzione completa (ad es. per applicare immediatamente gli aggiornamenti di sicurezza di sistema):```sh
./foxcage --rebuild

Temi

foxcage inoltra automaticamente dall'host gli elementi seguenti, così Firefox nel contenitore appare e si comporta come un'applicazione nativa:

  • Font. I font di sistema (/usr/share/fonts) e i font dell'utente (~/.local/share/fonts) sono montati in bind mount in sola lettura. Anche la configurazione dei font da ~/.config/fontconfig viene inoltrata.
  • Tema GTK e modalità scura. Rilevati tramite GTK_THEME o gsettings e passati al contenitore. La configurazione GTK da ~/.config/gtk-3.0 e ~/.config/gtk-4.0 è montata in sola lettura (bind mount).
  • Fuso orario. Il nome del fuso orario dell'host (rilevato da TZ, dal symlink /etc/localtime o da /etc/timezone) viene passato nel contenitore come TZ, e /etc/localtime viene montato in sola lettura (bind mount). Servono entrambi: Firefox deriva il fuso orario JavaScript dal nome della zona, non dal contenuto del file — senza TZ, i siti web mostrerebbero gli orari in UTC.
  • Locale. LANG viene inoltrata. La locale dell'host viene generata nell'immagine del contenitore in fase di build.

Etichetta della cage. La barra dei menu di Firefox mostra "FoxCage" (o "FoxCage - nome" per le cage denominate) così puoi capire a colpo d'occhio che sei in una sessione containerizzata. La barra dei menu è sempre visibile tramite i criteri aziendali.

Il contenitore include solo il tema GTK Adwaita. Su desktop GNOME funziona subito senza configurazione. Su KDE o altri desktop, Firefox ripiegherà su Adwaita se il tuo tema GTK (es. Breeze) non è installato nel contenitore. Il rilevamento della modalità scura funziona comunque finché la preferenza è impostata tramite gsettings o GTK_THEME.

DRM (Netflix, Disney+, ecc.)

Il DRM Widevine funziona senza configurazione. Alla prima visita a un sito protetto da DRM, Firefox scaricherà automaticamente il CDM Widevine. Potrebbe volerci un momento.

Integrazione con l'host (sempre attiva)

foxcage utilizza un proxy D-Bus filtrato per dare a Firefox accesso all'XDG Desktop Portal e al demone delle notifiche dell'host. Queste funzionalità sono sicure perché ogni accesso è mediato dall'utente: l'host mostra finestre di dialogo native con cui devi interagire. Un browser compromesso non può accedere silenziosamente alle risorse dell'host.

  • Caricamento file — selettore file nativo dell'host (sei tu a scegliere quali file condividere)
  • Link esternimailto:, link magnet, ecc. si aprono tramite il selettore di app dell'host
  • Notifiche desktop — inoltrate al demone delle notifiche dell'host
  • Condivisione schermo — selettore schermo del portal + flusso video PipeWire (richiede PipeWire sull'host)

Pass-through dei dispositivi (opt-in)

Queste funzionalità di pass-through passano i dispositivi dell'host direttamente nel contenitore e sono disattivate di default — a differenza delle funzionalità del portal sopra citate, non c'è alcuna conferma lato host. Un browser compromesso potrebbe usare l'hardware in modo silenzioso.```toml webcam = true # /dev/video* — webcam for video calls local_printers = true # CUPS socket — USB printers (network printers work by default) security_keys = true # /dev/hidraw* — FIDO2/U2F hardware keys

## Non ancora supportato

Alcune funzionalità delle piattaforme web non funzionano nel container a causa della mancata integrazione con l'host. Queste sono elencate qui per trasparenza.

**Bluetooth, USB, seriale e NFC.** Le API Web Bluetooth, WebUSB, Web Serial e WebNFC richiedono l'accesso ai dispositivi e servizi di sistema (BlueZ, udev) che non sono disponibili nel container.

**Gamepad e MIDI.** L'API Gamepad richiede l'accesso a `/dev/input/`. Web MIDI richiede l'accesso al sequencer ALSA. Nessuno dei due è inoltrato.

**Installazione di PWA.** Le Progressive Web App non possono essere installate sul desktop dell'host dall'interno del container.

**Accessibilità.** Il supporto per gli screen reader tramite AT-SPI è disabilitato (`NO_AT_BRIDGE=1`) — il container non ha alcuna connessione al bus di accessibilità dell'host. La sintesi vocale dell'API Web Speech funziona: `speech-dispatcher` con il motore `espeak-ng` è installato nella cage e viene avviato automaticamente al primo utilizzo, con l'audio instradato attraverso la socket PulseAudio condivisa.

## Configurazione dell'host

### Consigliato: storage overlay con fuse-overlayfs

Podman rootless può utilizzare di default il driver di storage `vfs`, che copia interi layer di immagini invece di usare mount overlay. Questo rende l'avvio del container dopo una build molto più lento. Per risolvere, installa `fuse-overlayfs` e aggiungi quanto segue a `~/.config/containers/storage.conf`:```toml
[storage]
driver = "overlay"

[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"

Impostare foxcage come browser predefinito

Per prima cosa, assicurati che lo script foxcage sia nella sua posizione permanente (ad es. ~/bin/foxcage o /usr/local/bin/foxcage). Il comando di installazione registra il percorso corrente dello script nel file .desktop, quindi spostarlo successivamente romperà il launcher.

Quindi esegui:```sh foxcage --install

Questo crea un file `.desktop` che punta alla posizione corrente dello script, installa l'icona foxcage e aggiorna i database del desktop e delle icone. FoxCage dovrebbe quindi apparire nel menu delle applicazioni.

Per impostare foxcage come browser web predefinito, in modo che i link cliccati in altre applicazioni si aprano in foxcage:```sh
xdg-settings set default-web-browser foxcage.desktop

Se una gabbia è già in esecuzione, gli URL si aprono come nuova scheda nel browser esistente.

Per annullare:```sh foxcage --uninstall

`StartupNotify=true` è impostato nel file `.desktop`, che dice al compositor di mostrare un cursore di caricamento mentre foxcage si avvia. Quando è necessaria una build dell'immagine (che può richiedere diversi minuti), foxcage invia una notifica desktop per farti sapere che Firefox è in arrivo. Qualsiasi errore di uscita anticipata (refuso di configurazione, dipendenza mancante, nome cage malformato) viene anche mostrato come notifica desktop, così gli utenti che lo avviano dal desktop non restano a fissare il vuoto quando foxcage fallisce senza un terminale collegato. Entrambi richiedono `notify-send` (da `libnotify-bin` su Debian/Ubuntu) — se non è installato, le notifiche vengono saltate silenziosamente e l'errore va comunque su stderr.

<details>
<summary>Configurazione manuale</summary>

Se preferisci creare il file `.desktop` manualmente, crea `~/.local/share/applications/foxcage.desktop`:```ini
[Desktop Entry]
Type=Application
Name=FoxCage
Comment=Firefox in a rootless Podman container
Exec=/path/to/foxcage %u
Icon=foxcage
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;
Terminal=false
Categories=Network;WebBrowser;
StartupNotify=true
StartupWMClass=foxcage

Sostituisci /path/to/foxcage con il percorso effettivo dello script. Registralo:```sh update-desktop-database ~/.local/share/applications

</details>

## Esecuzione dei test

La suite di test utilizza pytest + pytest-cov, dichiarati come dipendenze solo per lo sviluppo in `requirements-dev.txt`.```
pip install -r requirements-dev.txt
pytest

I test sono completamente ermetici — niente podman, niente rete, nessun filesystem reale oltre a tmp_path di pytest. La suite impone il 100% di copertura di righe e rami (configurata in pytest.ini e .coveragerc); qualsiasi riga non coperta, o lato di un condizionale non eseguito, fa fallire l'esecuzione. La CI esegue la suite a ogni push tramite .gitlab-ci.yml.

Ringraziamenti

Questo progetto è stato sviluppato da Mike Cardwell, con l'assistenza di Claude Code, lo strumento di coding AI di Anthropic.

Supporta/Apprezza il mio lavoro

Categorie