Voltar às atualizações
UpdatedAug 8, 2026

foxcage — Updated!

Execute o Firefox em um contêiner Podman rootless com capabilities removidas, rede isolada e armazenamento efêmero para conter fugas da sandbox e evitar o comprometimento do host.

Compartilhar

ícone do foxcage foxcage

Execute o Firefox em um contêiner Podman sem root para isolamento de segurança. Seu navegador é executado com quase nenhuma capacidade Linux, em seu próprio namespace de usuário e de rede, isolado do host — mantendo ainda aceleração de GPU completa, áudio e suporte a DRM.

Por que foxcage?

O Firefox já possui um sandbox multiprocesso que isola os renderizadores de conteúdo web usando namespaces Linux e seccomp-bpf. Para a maioria das ameaças, isso é eficaz. O foxcage adiciona uma segunda parede: se um invasor explorar uma vulnerabilidade que escape do sandbox do Firefox (o que acontece — existem CVEs para isso), ele cairá dentro de um contêiner fortemente restrito, em vez da sua sessão de usuário completa.

O que o foxcage protege contra

  • Acesso a arquivos pós-exploração. Uma fuga do sandbox no Firefox puro dá acesso a tudo que seu usuário pode ler: ~/.ssh, ~/.gnupg, perfis de navegador de outros navegadores, bancos de dados de gerenciadores de senhas, documentos, código-fonte. No foxcage, o invasor vê apenas o que você montou explicitamente.
  • Resíduo de rastreamento em disco. A gaiola efêmera @tmp não deixa nenhum vestígio em disco após o fechamento da janela — incluindo extensões, estado de HSTS, cache de sessão TLS e cache DNS que a Navegação Privada do Firefox ainda persiste. Várias gaiolas @tmp são executadas simultaneamente sem interferir umas com as outras.
  • Persistência. No Firefox puro, malware pode gravar em ~/.config/autostart, ~/.bashrc, cron ou em qualquer outro lugar para sobreviver a uma reinicialização. O contêiner efêmero (--rm) do foxcage significa que nada persiste a menos que você o tenha montado via bind.
  • Movimentação lateral na rede. Por padrão, o contêiner não pode sondar serviços em localhost. No Firefox puro, uma fuga do sandbox tem acesso total à rede. (Use [network] mode = "host" se uma gaiola precisar de acesso a localhost, por exemplo, para desenvolvimento local — mas veja a ressalva em "Rede": o modo host também expõe os sockets Unix abstratos do host.)
  • Escalação de privilégios. O contêiner remove todas as capacidades Linux, exceto CAP_SYS_CHROOT, e bloqueia a aquisição de novos privilégios. Binários setuid, explorações de kernel via syscalls obscuras e caminhos semelhantes de escalação são cortados.

O que o foxcage não protege contra

  • Ataques no nível do navegador. Phishing, extensões maliciosas e qualquer coisa que opere dentro da funcionalidade normal do Firefox permanecem inalterados — o foxcage isola o contêiner do host, não o usuário do navegador.
  • Diretórios montados via bind. Qualquer coisa que você monte (profile, downloads_dir, montagens bind adicionais) fica totalmente acessível a um navegador comprometido. Se você montar um diretório de perfil do host, um invasor poderá adulterá-lo exatamente como no Firefox puro.
  • Captura de áudio via PulseAudio. O socket do PulseAudio é montado via bind no contêiner. Embora seja montado somente leitura no nível do sistema de arquivos, os sockets de domínio Unix são bidirecionais — um processo comprometido ainda pode enviar solicitações de gravação pelo socket. Uma fuga do sandbox do navegador poderia potencialmente gravar áudio do microfone do host.
  • Explorações do compositor Wayland. O socket Wayland é repassado. Os compositores Wayland isolam clientes uns dos outros por design, mas uma vulnerabilidade no próprio compositor seria acessível.

Configuração de segurança

O contêiner é executado com:

  • Todas as capacidades Linux removidas (apenas CAP_SYS_CHROOT adicionado novamente para o sandbox de conteúdo do Firefox; CAP_SETUID/CAP_SETGID adicionados temporariamente quando init.root está configurado)
  • no-new-privileges para evitar escalação de privilégios
  • Namespace de usuário sem root (--userns keep-id)
  • /dev/shm privado (não compartilhado com o host) — tamanho configurável via shm_size
  • Rede isolada via pasta com loopback do host bloqueado por padrão
  • O DNS usa o DNS do host por padrão (configurável via network.dns)
  • Apenas sockets específicos de XDG_RUNTIME_DIR são montados via bind (Wayland, PulseAudio, PipeWire e o proxy D-Bus filtrado) — o diretório de runtime completo do host nunca é exposto
  • O acesso ao barramento de sessão D-Bus do host é sempre mediado por um xdg-dbus-proxy filtrado em execução no host. Apenas org.freedesktop.Notifications, org.freedesktop.portal.Desktop, org.mozilla.* e (para forks) o próprio namespace do fork (por exemplo, org.librewolf.*) são alcançáveis — serviços de sessão como o chaveiro e o agente SSH/GPG são bloqueados
  • O acesso ao portal é amplo. org.freedesktop.portal.Desktop é permitido como um todo, porque é assim que o seletor de arquivos, "abrir link em outro aplicativo" e o compartilhamento de tela funcionam. Ele também expõe RemoteDesktop (teclado/mouse sintético para toda a sessão), Camera e Location. Esses são controlados pelos diálogos de aprovação do seu desktop, e não pelo foxcage — e o prompt de RemoteDesktop se assemelha ao prompt de compartilhamento de tela, portanto leia os diálogos de aprovação antes de aceitá-los. xdg-dbus-proxy não tem uma regra de "negar uma interface", então restringir isso significa enumerar cada interface que o Firefox precisa; veja docs/DESIGN.md para saber por que isso não é feito por padrão
  • Todas as montagens bind (profile, downloads_dir, [mounts] bind extras) usam nosuid,noexec
  • Download do navegador verificado contra assinaturas GPG: Firefox contra as somas de verificação SHA-512 assinadas da Mozilla, LibreWolf contra a assinatura separada dos Mantenedores do LibreWolf mais a SHA-256 correspondente
  • Contêiner efêmero (--rm) — gravações no sistema de arquivos são perdidas ao sair
  • Nenhum dispositivo do host (webcam, chaves de segurança, impressoras) é repassado, a menos que seja explicitamente ativado

Cada opção [network] e [mounts] que você ativa troca um pouco de isolamento por conveniência. Os padrões são a configuração mais restritiva que ainda oferece um navegador utilizável.

Requisitos

  • Python 3.11+
  • Podman (sem root)
  • Compositor Wayland (X11 não é suportado)
  • pasta (sudo apt install passt) — a menos que network.mode = "host"
  • xdg-dbus-proxy (sudo apt install xdg-dbus-proxy)
  • PulseAudio ou PipeWire com compatibilidade com PulseAudio (para áudio)
  • GPU com suporte a DRI — opcional; sem /dev/dri, o foxcage avisa e o Firefox renderiza via software

Execute o foxcage como seu usuário normal do desktop, não como root ou via sudo — o sandbox mapeia seu usuário para dentro do contêiner, e executar como root remove o isolamento que o foxcage existe para fornecer. Ele se recusa a iniciar como root.

Ambiente testado: Debian 13 (Trixie) com GNOME 3. Outras distribuições Linux e compositores Wayland podem funcionar, mas não foram testados.

Instalação

foxcage é um único script Python, sem dependências fora da biblioteca padrão do Python. Copie-o para um diretório no seu PATH:```sh sudo cp foxcage /usr/local/bin/foxcage

Ou para uma instalação local do usuário:```sh
cp foxcage ~/.local/bin/foxcage

Certifique-se de que o script é executável (chmod +x foxcage).

Verifique qual revisão você tem com foxcage --version — útil ao relatar um problema, já que o foxcage é instalado copiando um único arquivo.

Uso```sh

./foxcage

Na primeira execução, o script cria a imagem do contêiner (baixa o Firefox da Mozilla, instala dependências mínimas do Debian) e então inicia o Firefox. Nas execuções seguintes, o foxcage verifica se há atualizações do Firefox e reconstrói a imagem automaticamente quando uma nova versão está disponível. A imagem também é reconstruída periodicamente (a cada 7 dias por padrão) para incorporar atualizações de pacotes do sistema. Se a verificação de atualização falhar (erro de rede, tempo esgotado), um aviso é registrado e a imagem existente é usada — o início nunca é bloqueado.

Passe argumentos para o Firefox:```sh
./foxcage https://example.com

Combine uma cage nomeada com as flags do Firefox:```sh ./foxcage @work --kiosk https://example.com

If a cage is already running, the URL opens in a new tab in the existing browser instead of starting a second container. Running `foxcage` (or `foxcage @cage`) with no URL against a running cage exits cleanly with a "cage is already running" message — foxcage can't raise an existing Wayland window from outside the container, so it doesn't try.

Per-launch flags do **not** apply when a cage is already running. `--dns`, `--ipv4-only`, `--lifetime`, `--color` and `--fork` are consumed when the container starts, and a running container's settings can't be changed from outside, so they are ignored with a warning. Close the cage and re-run to apply them.

> Use the `private_browsing` config key for private-mode sessions — *not* Firefox's raw `--private-window` CLI flag. The config key sets session-wide private mode (`browser.privatebrowsing.autostart`), so subsequent `foxcage @cage URL` invocations can reopen in tabs. `--private-window` as a Firefox passthrough would make only the first window private and break the reopen-in-tab behaviour above.
>
> **Heads up:** sessions enabled via `private_browsing = true` don't show Firefox's usual private-window UI cues (purple accent bar, mask icon, "(Private Browsing)" in the title). That's because every window in the session is private, so Firefox has no non-private window to visually contrast against — it suppresses the indicator. The session *is* genuinely private; verify if you want by visiting `about:privatebrowsing` in the cage (shows the standard Private Browsing info page) or `about:config` and checking `browser.privatebrowsing.autostart = true`.

### Ephemeral browsing with `@tmp`

For one-off links that should leave no trace, use the reserved `tmp` cage:```sh
./foxcage @tmp https://somewhere-suspicious.example

Cada inicialização @tmp é um Firefox novo e descartável, sem perfil persistente. Quando a janela fecha, tudo desaparece — cookies, cache, histórico, extensões, estado HSTS, cache de sessão TLS, cache DNS, estado de separadores guardados. Isto vai além da Navegação Privada do Firefox, que ainda persiste extensões e uma quantidade considerável de estado em disco.

Várias jaulas @tmp são executadas em simultâneo, cada uma isolada das outras. A barra de menus mostra FoxCage - tmp (<short id>) para que possa distinguir janelas efémeras simultâneas.

As jaulas efémeras abrem uma página em branco no arranque e separadores novos em branco — a página inicial padrão do Firefox e o conteúdo de novo separador (sites populares, recomendações Pocket, feed de atividade) são puro ruído num perfil novo que está prestes a ser descartado, por isso são suprimidos. As jaulas persistentes mantêm os padrões do Firefox.

Jaulas efémeras nomeadas

Se pretender um nome significativo numa sessão descartável (por exemplo, um buraco de coelho de pesquisa que queira reabrir num novo separador), utilize @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>` ainda é efêmero — quando você fecha a janela, tudo desaparece. A diferença em relação ao `@tmp` simples é que uma segunda inicialização com o mesmo nome **reutiliza a janela existente** (igual às gaiolas persistentes), permitindo adicionar mais abas depois sem iniciar uma cópia paralela. O `@tmp` simples mantém o comportamento de "cada lançamento é um descartável novo".

O rótulo da barra de menu mostra o nome que você escolheu (`FoxCage - tmp-research`), então a janela é rotulada de forma significativa.

#### Personalizando padrões efêmeros

Crie `~/.config/foxcage/tmp.toml` para definir padrões para todas as gaiolas efêmeras (tanto o `@tmp` simples quanto cada `@tmp-<name>`). Por exemplo:```toml
private_browsing = true
lifetime = "30m"

[network]
dns = "cloudflare"

Cada lançamento efêmero agora obtém uma janela privada, Cloudflare DoH e fecha automaticamente após 30 minutos — com a efemeridade totalmente intacta. Efêmeros nomeados herdam tmp.toml por padrão; se quiser substituir por nome, crie ~/.config/foxcage/tmp-<name>.toml. Esse arquivo então é aplicado em vez de tmp.toml — sem mesclagem, o arquivo mais específico vence diretamente. Copie os padrões compartilhados para ele se quiser.

Qualquer coisa que você possa definir na configuração de uma gaiola regular funciona aqui, exceto a única chave que anularia a própria efemeridade:

  • profile — erro fatal.

Ela aponta para um diretório de perfil persistente no host, o que contradiz diretamente o propósito de @tmp. Se você quiser uma gaiola com sandbox e perfil persistente, use uma gaiola nomeada regular (@work, @research, etc.) que não comece com tmp-.

Substituindo DNS por lançamento

A flag --dns (e a chave de configuração equivalente network.dns) aceita três formas:```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 o valor corresponde a um provedor conhecido (por alias ou por IP), o foxcage ativa automaticamente o DNS forçado sobre HTTPS para esse provedor.** O TRR do Firefox é definido para o modo 3 (estrito, sem fallback em texto simples) com o endereço de bootstrap preenchido para que não haja vazamento de resolução não criptografada na inicialização. Você vê um aviso de uma linha no stderr como `Enabling DNS over HTTPS via Cloudflare`.

Aliases integrados:

| Alias | IP | Filtragem |
|-------|------|-----------|
| `cloudflare` | 1.1.1.1 | nenhuma |
| `cloudflare-security` | 1.1.1.2 | bloqueia malware |
| `cloudflare-family` | 1.1.1.3 | bloqueia malware + adulto |
| `google` | 8.8.8.8 | nenhuma |
| `quad9` | 9.9.9.9 | bloqueia malware (padrão do Quad9) |
| `quad9-unfiltered` | 9.9.9.10 | nenhuma |
| `adguard` | 94.140.14.14 | bloqueia anúncios + rastreadores |
| `adguard-family` | 94.140.14.15 | anúncios + rastreadores + adulto |
| `opendns` | 208.67.222.222 | alguma |

Um IP que não está na tabela (por exemplo, o Pi-hole da sua LAN) permanece apenas em texto simples — nenhum DoH é ativado, pois o foxcage não conhece o endpoint DoH correspondente. Use o formato de URI para isso: `--dns https://pi.hole/dns-query` (com um certificado válido) ativa o DoH e deixa o DNS do contêiner intocado.

O formato de URI não define o DNS de texto simples do contêiner, então qualquer coisa dentro do contêiner que não seja o Firefox ainda usa o DNS do host. Isso é proposital — `--dns URI` significa "faça o Firefox usar este resolvedor DoH", ponto final.

`--dns` é incompatível com `network.mode = "host"`, que já tem acesso total à rede do host.

### Identificação visual das cages

Cada cage nomeada recebe uma cor de destaque na barra de menus para que você possa distinguir as janelas rapidamente. **Você não precisa configurar nada** — a cor é derivada deterministicamente do nome da cage (hash SHA256 convertido em um matiz, com saturação e luminosidade fixas). `@banking`, `@work`, `@personal`, `@tmp-research` recebem cores distintas e estáveis sem você mover um dedo.

A cage padrão (anônima) mantém o laranja embutido.

Se você quiser substituir a cor derivada automaticamente, defina-a explicitamente:```toml
# ~/.config/foxcage/banking.toml
color = "#dc2626"   # red — overrides the auto-derived colour

Atualizar Wiki do Github

[!TIP] Este fluxo de trabalho atualiza automaticamente o wiki do seu repositório github com base em um arquivo home.md durante um fluxo de trabalho relacionado ao Wiki.

Arquivos

  • .github/workflows/update-wiki.yml - Este é o arquivo de fluxo de trabalho responsável por manter o wiki atualizado.
...
``````sh
./foxcage @experiment --color "#10b981" https://example.com   # teal, one-off

Aceita hex CSS padrão: #rgb, #rrggbb ou #rrggbbaa (com alfa). As cores derivadas automaticamente são ajustadas para serem visíveis em barras de menu claras e escuras (luminosidade fixa em 55%, saturação em 75%), então você não deve precisar sobrescrevê-las por motivos de tema.

Gaiolas com limite de tempo

A flag --lifetime (e a chave de configuração equivalente lifetime) fecha uma gaiola automaticamente após uma duração definida. O formato é <number><unit> com unidade s, m ou h:```sh ./foxcage @tmp --lifetime 10m https://example.com ./foxcage @work --lifetime 2h

A contagem regressiva começa quando o Firefox realmente inicia dentro da cage — o tempo de inicialização do contêiner e de construção da imagem não consomem seu orçamento. O rótulo da barra de menus da cage mostra a contagem regressiva junto com a identidade da cage — por exemplo, `FoxCage - tmp (a3f2b1) | 9m` — atualizado uma vez por minuto enquanto houver mais de um minuto restante, e uma vez por segundo no último minuto. Quando a contagem regressiva chega a zero, o Firefox se fecha e o contêiner é encerrado. Se você fechar o Firefox por conta própria antes do tempo de vida acabar, nada de incomum acontece.

Defina um tempo de vida padrão por cage na sua configuração:```toml
# ~/.config/foxcage/tmp.toml — every @tmp launch auto-closes after 15 minutes
lifetime = "15m"
private_browsing = true

--lifetime na linha de comando prevalece sobre qualquer valor de configuração.

Force uma reconstrução completa da imagem (baixa novamente o Firefox e todos os pacotes do sistema):```sh ./foxcage --rebuild

Um contêiner em execução mantém a imagem a partir da qual foi iniciado, mesmo depois que o foxcage reconstrói a tag da imagem. Se você tentar abrir uma aba em uma cage cuja imagem foi atualizada desde então (por `--rebuild`, uma atualização do Firefox ou a reconstrução agendada), o foxcage recusa com um erro (também exibido como notificação de desktop) e pede que você saia do Firefox e o reabra — o que inicia um novo contêiner na imagem atual. Sob `--rebuild` com uma cage ativa, o foxcage avisa antecipadamente, realiza a construção e então aplica a mesma verificação.

### Atualizações

O foxcage verifica novas versões do navegador a cada inicialização — a API de lançamentos da Mozilla para o Firefox, o endpoint de releases do GitLab para o LibreWolf. Se uma atualização estiver disponível, a imagem do contêiner é reconstruída automaticamente. A imagem também é reconstruída periodicamente (a cada 7 dias por padrão) para incorporar atualizações de segurança do Debian. O atualizador automático embutido do navegador é desabilitado, pois as atualizações são tratadas no nível da imagem.

Se a verificação de atualização falhar (sem rede, tempo limite da API), um aviso é exibido e a imagem existente é usada — você sempre pode navegar.

A cadência de atualizações fica no nível superior da configuração; o pinning de versão e canal fica na seção por 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)

Fixar uma versão ESR também exige o canal. O índice de versões da Mozilla lista as versões ESR sem o sufixo esr que seus downloads carregam, então um simples version = "140" no canal padrão resolve para uma versão que não existe. Defina ambos:```toml [firefox] channel = "esr" version = "140" # → 140.13.0esr

Um pin que não corresponde a nenhuma release é agora um erro que nomeia o pin, em vez de cair silenciosamente para a release mais recente. Uma falha temporária ao alcançar a API da Mozilla ainda emite um aviso e continua com a imagem existente, para que uma rede instável nunca bloqueie a inicialização.

Pins com sufixo devem ser totalmente qualificados — `"140.13.0esr"` e `"150.0b9"` funcionam, `"140esr"` e `"150b9"` são rejeitados no carregamento da configuração porque nenhuma release pode corresponder a eles. O mesmo se aplica às revisões do LibreWolf: `"146.0.1-1"` funciona, `"146-1"` não.

Para forçar uma reconstrução completa imediata: `./foxcage --rebuild`

### Forks do Firefox (LibreWolf)

foxcage pode executar um fork do Firefox orientado à privacidade em vez do Firefox upstream:```toml
fork = "librewolf"         # default is "firefox"

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

Ou por execução via CLI:```sh foxcage @tmp --fork librewolf https://example.com

**LibreWolf**: fork do Firefox endurecido para privacidade — proteção rigorosa contra rastreamento, DoH, RFP, telemetria desativada por padrão. Tarball Linux assinado do GitLab (`librewolf-community/browser/bsys6`), verificado via GPG com a chave dos Mantenedores do LibreWolf `662E 3CDD 6FE3 2900 2D0C A5BB 4033 9DD8 2B12 EF16` e com verificação cruzada do `.sha256sum` correspondente. O `librewolf.cfg` incluído no LibreWolf é preservado; o foxcage adiciona suas próprias prefs por cima, em vez de sobrescrever.

**O canal é exclusivo para Firefox**: `firefox.channel = "beta" | "esr"` é rejeitado quando `fork` é qualquer coisa diferente de `"firefox"`. O LibreWolf tem um único canal de lançamento.

Alternar `fork` (via config ou `--fork`) altera o hash do Containerfile, o que aciona uma reconstrução no próximo lançamento — não é necessário `--rebuild` manual.

#### Compatibilidade de perfis

> **Use um perfil dedicado para cada fork.** O padrão mais seguro é deixar o foxcage provisionar seu próprio perfil (omitir `profile` da configuração) ou apontar `profile` para um diretório que você também não abra a partir do host.

- **LibreWolf**: *geralmente* é seguro compartilhar com seu perfil do Firefox do host — o LibreWolf acompanha as versões do Firefox em poucos dias, então conflitos de esquema do `compatibility.ini` são raros. Riscos: (1) apenas o uso sequencial é seguro (o arquivo de bloqueio do Firefox impede aberturas simultâneas); (2) na pequena janela após um lançamento estável do Firefox, executar o Firefox primeiro e depois o LibreWolf pode acionar um diálogo de migração "usado por uma versão mais recente"; (3) recursos que o LibreWolf remove (Sync, Pocket, conta Mozilla) silenciosamente não funcionam, mas não corrompem dados.

### Cages nomeadas

Execute instâncias sandbox separadas com sua própria configuração e perfil do Firefox:```sh
./foxcage @work

Isso carrega ~/.config/foxcage/work.toml e usa uma imagem separada (foxcage-work), contêiner (foxcage-work) e volume (foxcage-work-profile). O arquivo de configuração deve existir para cages nomeados. Nomes de cages podem conter apenas letras, dígitos, hífens e sublinhados.

Configuração

Os arquivos de configuração ficam em $XDG_CONFIG_HOME/foxcage/ (padrão: ~/.config/foxcage/).

  • config.toml — cage padrão (opcional, com padrões sensatos sem ele)
  • <name>.toml — cage nomeado, carregado com @<name> (obrigatório)

Chaves de configuração desconhecidas são rejeitadas com um erro. Veja config.toml.example para todas as opções disponíveis com padrões.

Exemplo de 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 ( 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 "-",

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"]

### Perfil do Firefox do anfitrião

Para partilhar um perfil do Firefox do anfitrião com a gaiola, defina `profile` para o diretório do perfil. Encontre o caminho do seu perfil visitando `about:profiles` no Firefox no anfitrião — ou aponte para um diretório vazio novo se quiser que a gaiola comece com um perfil limpo que persiste no anfitrião.```toml
profile = "~/.mozilla/firefox/xxxxxxxx.default-release"

Only this one directory is bind-mounted into the cage. Sibling profiles under ~/.mozilla/firefox/ and the profiles.ini registry are not exposed — a compromised cage cannot tamper with them.

If profile is unset, a named Podman volume stores the Firefox profile instead (see "What persists" below). If the same profile is already open in Firefox on the host, Firefox's per-profile lock file will cause a clash — use a dedicated profile per cage.

Networking

By default, the container uses pasta with host loopback blocked and host DNS. pasta requires podman 4.4 or newer (it has been the rootless default since podman 5.0).

Host networking removes network isolation entirely. Use this when the cage needs to reach services on the host's localhost (e.g. a local dev server, a database on 127.0.0.1):```toml [network] mode = "host"

`dns` não pode ser combinado com `mode = "host"` — a rede do host já usa o resolvedor do host.

> **O modo host abre mão de mais do que apenas localhost.** Ele coloca a jaula no namespace de rede do host, e os sockets Unix abstratos têm escopo nesse namespace em vez de no sistema de arquivos. Então, uma jaula no modo host pode alcançar sockets de endereço abstrato no host diretamente — incluindo o `@/tmp/.X11-unix/X0` do Xwayland se você executar X11 ou Xwayland (registro de entrada, apesar de o foxcage ser somente Wayland), e um barramento de sessão configurado com `unix:abstract=…`, o que contornaria o proxy D-Bus filtrado. Isso é inerente ao compartilhamento da pilha de rede, não algo que o foxcage possa filtrar. Use o modo host quando precisar dele e prefira uma jaula nomeada que você inicie apenas para esse fim.

**Jaulas somente IPv4** desabilitam o IPv6 inteiramente:```toml
[network]
ipv4_only = true

Ou por execução com a flag --ipv4-only (forma curta -4, como em ssh/curl/pasta):```sh ./foxcage @tmp -4 https://example.com

This runs pasta in IPv4-only mode (`-4`), so the container has no IPv6 stack at all, and additionally sets `network.dns.disableIPv6` in Firefox so it doesn't resolve AAAA records — which matters when DoH is enabled, since DoH answers bypass the container's resolver. `ipv4_only` cannot be combined with `mode = "host"` — host networking uses the host's network stack directly, so disable IPv6 on the host instead.

### Comandos de inicialização

Execute comandos personalizados no momento da construção da imagem ou na inicialização do container via `[init]`:

- **`build`** — executa no momento da construção da imagem como root. Use para instalar pacotes ou outras configurações lentas. Alterações nos comandos de build acionam automaticamente uma reconstrução da imagem.
- **`root`** — executa na inicialização do container como root, antes do Firefox. Use para tarefas rápidas de root em tempo de execução (ajustar permissões, escrever arquivos de configuração).
- **`user`** — executa na inicialização do container como seu usuário, antes do Firefox. Use para criar diretórios, configurar estado em nível de usuário.```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"]

As três chaves são listas de strings de comandos de shell. Se qualquer comando falhar, o contêiner encerra sem iniciar o Firefox.

Nota de segurança: Quando init.root está definido, o contêiner inicia como root com CAP_SETUID e CAP_SETGID adicionados (além do padrão CAP_SYS_CHROOT) para poder reverter ao usuário regular. Essas capacidades são mantidas apenas durante a fase de init root — após a queda de privilégios, o processo do usuário regular não tem capacidades extras. Sem init.root, o contêiner executa com o conjunto de capacidades mínimas padrão.

O que persiste

Sem configuração, um volume Podman nomeado armazena o perfil do Firefox (favoritos, configurações, extensões, plugin Widevine DRM). Todo o resto é efêmero.

  • Gaiola padrão: foxcage-profile
  • Gaiola nomeada: foxcage-<name>-profile

Para começar do zero, remova o volume:```sh podman volume rm foxcage-profile

Se `profile` estiver definido, o diretório do host é montado diretamente via bind mount e nenhum volume é criado.

### Uso de disco

Cada imagem de cage tem cerca de 1 GB. Um rebuild redefine a tag da imagem e deixa a anterior para trás como uma entrada sem tag `<none>`, então o foxcage remove a imagem que acabou de deslocar após cada build bem-sucedido. Ele remove apenas essa imagem específica, e nunca uma que um cage em execução ainda esteja usando.

Imagens órfãs antes desse comportamento existir não são limpas retroativamente. Para recuperá-las:```sh
podman images --filter dangling=true    # review first
podman image prune                      # then remove

As atualizações do Firefox são detectadas automaticamente a cada inicialização. Para forçar uma reconstrução completa (por exemplo, para aplicar imediatamente as atualizações de segurança do sistema):```sh ./foxcage --rebuild

## Theming

O foxcage encaminha automaticamente o seguinte a partir do host, para que o Firefox no contêiner tenha a aparência e o comportamento de um aplicativo nativo:

- **Fontes.** As fontes do sistema (`/usr/share/fonts`) e as fontes do usuário (`~/.local/share/fonts`) são montadas via bind como somente leitura. A configuração de fontes de `~/.config/fontconfig` também é encaminhada.
- **Tema GTK e modo escuro.** Detectado via `GTK_THEME` ou `gsettings` e passado ao contêiner. A configuração do GTK de `~/.config/gtk-3.0` e `~/.config/gtk-4.0` é montada via bind como somente leitura.
- **Fuso horário.** O nome do fuso horário do host (detectado a partir de `TZ`, do symlink `/etc/localtime` ou de `/etc/timezone`) é passado para o contêiner como `TZ`, e `/etc/localtime` é montado via bind como somente leitura. Ambos são necessários: o Firefox deriva o fuso horário do JavaScript a partir do *nome* do fuso, não do conteúdo do arquivo — sem `TZ`, os sites mostrariam as horas em UTC.
- **Locale.** `LANG` é encaminhado. O locale do host é gerado na imagem do contêiner em tempo de build.

**Rótulo do cage.** A barra de menus do Firefox exibe "FoxCage" (ou "FoxCage - nome" para cages nomeados) para que você saiba rapidamente que está em uma sessão conteinerizada. A barra de menus está sempre visível por meio de política empresarial.

O contêiner inclui apenas o tema GTK Adwaita. Em desktops GNOME, isso funciona imediatamente. No KDE ou em outros desktops, o Firefox recorrerá ao Adwaita se o seu tema GTK (ex.: Breeze) não estiver instalado no contêiner. A detecção de modo escuro ainda funciona desde que a preferência seja definida via `gsettings` ou `GTK_THEME`.

## DRM (Netflix, Disney+ etc.)

O Widevine DRM funciona imediatamente. Na primeira visita a um site protegido por DRM, o Firefox baixará o Widevine CDM automaticamente. Isso pode levar alguns instantes.

## Integração com o host (sempre ativa)

O foxcage usa um proxy D-Bus filtrado para dar ao Firefox acesso ao XDG Desktop Portal e ao daemon de notificações do host. Esses recursos são seguros porque todo acesso é mediado pelo usuário — o host mostra diálogos nativos com os quais você precisa interagir. Um navegador comprometido não pode acessar recursos do host silenciosamente.

- **Upload de arquivos** — seletor de arquivos nativo do host (você escolhe quais arquivos compartilhar)
- **Links externos** — `mailto:`, links magnet etc. abrem via seletor de aplicativos do host
- **Notificações da área de trabalho** — encaminhadas ao daemon de notificações do host
- **Compartilhamento de tela** — seletor de tela do portal + stream de vídeo PipeWire (requer PipeWire no host)

## Passagem de dispositivos (opt-in)

Estes recursos passam dispositivos do host diretamente para o contêiner e estão **desativados por padrão** — diferentemente dos recursos de portal acima, não há confirmação no lado do host. Um navegador comprometido poderia usar o hardware silenciosamente.```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

Ainda não suportado

Alguns recursos da plataforma web não funcionam no contêiner devido à falta de integração com o host. Eles estão listados aqui para transparência.

Bluetooth, USB, serial e NFC. As APIs Web Bluetooth, WebUSB, Web Serial e WebNFC exigem acesso a dispositivos e serviços de sistema (BlueZ, udev) que não estão disponíveis no contêiner.

Gamepads e MIDI. A API Gamepad precisa de acesso a /dev/input/. A Web MIDI precisa de acesso ao sequenciador ALSA. Nenhum deles é repassado.

Instalação de PWA. Progressive Web Apps não podem ser instalados na área de trabalho do host de dentro do contêiner.

Acessibilidade. O suporte a leitores de tela via AT-SPI está desabilitado (NO_AT_BRIDGE=1) — o contêiner não tem conexão com o barramento de acessibilidade do host. A síntese da Web Speech API funciona: o speech-dispatcher com o mecanismo espeak-ng está instalado no cage e é iniciado automaticamente no primeiro uso, com o áudio roteado pelo socket PulseAudio compartilhado.

Configuração do host

Recomendado: armazenamento overlay com fuse-overlayfs

O Podman rootless pode usar por padrão o driver de armazenamento vfs, que copia camadas inteiras da imagem em vez de usar montagens overlay. Isso torna a inicialização do contêiner muito mais lenta após um build. Para corrigir isso, instale fuse-overlayfs e adicione o seguinte em ~/.config/containers/storage.conf:```toml [storage] driver = "overlay"

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

### Definindo o foxcage como seu navegador padrão

Primeiro, certifique-se de que o script `foxcage` está em seu local permanente (ex.: `~/bin/foxcage` ou `/usr/local/bin/foxcage`). O comando de instalação registra o caminho atual do script no arquivo `.desktop`, portanto, movê-lo depois quebrará o lançador.

Em seguida, execute:```sh
foxcage --install

Isto cria um ficheiro .desktop que aponta para a localização atual do script, instala o ícone do foxcage e atualiza as bases de dados do ambiente de trabalho e de ícones. O FoxCage deverá então aparecer no seu menu de aplicações.

Para definir o foxcage como o navegador web predefinido, para que as ligações clicadas noutras aplicações abram no foxcage:```sh xdg-settings set default-web-browser foxcage.desktop

Se uma gaiola já estiver em execução, os URLs abrem como uma nova aba no navegador existente.

Para desfazer:```sh
foxcage --uninstall

StartupNotify=true está definido no arquivo .desktop, o que informa ao compositor para mostrar um cursor de spinner enquanto o foxcage inicia. Quando uma compilação de imagem é necessária (o que pode levar vários minutos), o foxcage envia uma notificação de desktop para que você saiba que o Firefox está a caminho. Qualquer erro de saída antecipada (erro de digitação na configuração, dependência ausente, nome de cage malformado) também é exibido como uma notificação de desktop para que usuários que iniciaram pelo desktop não fiquem olhando para nada quando o foxcage falhar sem um terminal anexado. Ambos exigem notify-send (de libnotify-bin no Debian/Ubuntu) — se não estiver instalado, as notificações são silenciosamente ignoradas e o erro ainda vai para o stderr.

Configuração manual

Se você preferir criar o arquivo .desktop manualmente, crie ~/.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

Substitua `/path/to/foxcage` pelo caminho real para o script. Registre-o:```sh
update-desktop-database ~/.local/share/applications

Executando testes

A suíte de testes usa pytest + pytest-cov, declarados como dependências apenas de desenvolvimento em requirements-dev.txt.``` pip install -r requirements-dev.txt pytest

Os testes são totalmente herméticos — sem podman, sem rede, sem sistema de arquivos real além do `tmp_path` do pytest. A suíte exige **100% de cobertura de linhas e ramificações** (configurado em `pytest.ini` e `.coveragerc`); qualquer linha não coberta, ou lado não executado de uma condicional, faz a execução falhar. A CI executa a suíte a cada push via `.gitlab-ci.yml`.

## Agradecimentos

Este projeto foi desenvolvido por Mike Cardwell, com a assistência de [Claude Code](https://claude.ai/claude-code), a ferramenta de codificação por IA da Anthropic.

## Apoie/Aprecie meu trabalho

- [Bitcoin](bitcoin:1PQLtWnjUi1itHLG6QCQeHM3Nxua8pRsq1): 1PQLtWnjUi1itHLG6QCQeHM3Nxua8pRsq1
- [Paypal](https://www.paypal.me/grepular)
- [Patreon](https://patreon.com/grepular)

Categorias