Aula 13: Jails FreeBSD — containers nativos de alta segurança

Aula 13: Jails FreeBSD — containers nativos de alta segurança

Bem-vindo à décima terceira aula do curso “FreeBSD — Do Zero ao Avançado”. Hoje vamos mergulhar em um dos recursos mais poderosos e distintivos do FreeBSD: as Jails FreeBSD. Se você já ouviu falar em containers Linux como Docker ou LXC, prepare-se para conhecer a tecnologia que inspirou grande parte do ecossistema de conteinerização moderna — e que, em muitos aspectos, continua sendo superior em isolamento e segurança. As Jails FreeBSD são um mecanismo de virtualização em nível de sistema operacional que permite particionar uma única instância do FreeBSD em múltiplos ambientes isolados, cada um com seu próprio conjunto de usuários, processos, árvore de diretórios e pilha de rede.

Diferentemente de máquinas virtuais completas, as jails compartilham o mesmo kernel do sistema hospedeiro (host), o que as torna extremamente leves e rápidas — o overhead é mínimo, praticamente zero. No entanto, ao contrário de soluções como chroot puro, as jails impõem restrições rigorosas que impedem que um processo dentro da jail escape para o sistema host ou interfira com outras jails. Estamos falando de um isolamento real, testado em ambientes de produção há mais de duas décadas. Em nossos projetos na JRT Technology Solutions, utilizamos Jails FreeBSD diariamente para isolar serviços críticos, criar ambientes de desenvolvimento descartáveis e até mesmo hospedar aplicações de clientes em um único servidor físico com total separação lógica.

Nesta aula, você vai aprender a criar, configurar e gerenciar Jails FreeBSD do zero, utilizando tanto o método clássico com jail.conf quanto a ferramenta moderna iocage, que simplifica a administração com comandos intuitivos e integração nativa com ZFS. Vamos cobrir desde a preparação do sistema host — incluindo a configuração de datasets ZFS e interfaces de rede virtuais — até a instalação de um sistema FreeBSD completo dentro da jail, a definição de limites de recursos e a exposição de serviços para a rede externa.

Ao final desta aula, você será capaz de isolar aplicações com segurança de nível enterprise, construir ambientes de teste idênticos à produção e entender profundamente como o FreeBSD implementa conteinerização de forma nativa, sem necessidade de software de terceiros. Este conhecimento é diretamente aplicável em cenários reais de infraestrutura, segurança da informação e DevOps. Prepare seu terminal, acesso root e um ambiente FreeBSD 14 ou superior — vamos começar.

O que você vai aprender nesta aula

  • Os fundamentos teóricos das Jails FreeBSD: como funcionam, quais camadas de isolamento oferecem e como se comparam a outras tecnologias de virtualização
  • Preparar o sistema host com datasets ZFS dedicados e interfaces de rede bridge para as jails
  • Criar uma jail base (template) com o sistema FreeBSD completo usando bsdinstall e fetch
  • Configurar e iniciar jails manualmente com /etc/jail.conf, entendendo cada parâmetro de segurança e isolamento
  • Utilizar o iocage para gerenciar jails de forma moderna, com snapshots ZFS, clonagem e rede simplificada
  • Verificar o funcionamento correto das jails, testar conectividade de rede e executar processos isolados
  • Diagnosticar e corrigir os erros mais comuns encontrados por administradores iniciantes e intermediários
  • Aplicar boas práticas de hardening e otimização que utilizamos em deploy profissionais na JRT Technology Solutions

Pré-requisitos e Ambiente

Antes de iniciar os procedimentos práticos, certifique-se de que seu ambiente atende aos seguintes requisitos. Assumimos que você concluiu as aulas anteriores do curso e possui um sistema FreeBSD 14.0-RELEASE ou superior instalado e atualizado. O sistema deve ter o ZFS como sistema de arquivos raiz — esta é a configuração padrão do instalador do FreeBSD e será essencial para aproveitar snapshots e clonagem eficiente. Você precisará de acesso root ou privilégios de sudo para executar todos os comandos desta aula.

  • Sistema Operacional: FreeBSD 14.0-RELEASE ou superior (amd64)
  • Sistema de arquivos: ZFS como root filesystem (zroot)
  • Acesso: conta root ou usuário no grupo wheel com sudo configurado
  • Rede: pelo menos uma interface de rede física (ex: em0, igb0, re0) com conectividade à Internet para download dos sets de instalação
  • Espaço em disco: mínimo de 5 GB livres no pool ZFS para a jail base e jails derivadas
  • Conhecimentos prévios: operação básica do FreeBSD, edição de arquivos com vi/ee/nano, comandos ZFS básicos (zfs list, zfs create), configuração de rede com ifconfig

Execute o comando abaixo para verificar se seu sistema está pronto. A saída deve confirmar a versão do FreeBSD, a presença do ZFS e o estado da interface de rede.

# Verificar versão do FreeBSD
freebsd-version -k
# Deve retornar algo como: 14.0-RELEASE-p5

# Verificar pools ZFS
zpool list
# Deve mostrar zroot ou outro pool ativo

# Verificar interfaces de rede
ifconfig | grep -E "^[a-z]+[0-9]+:" | cut -d: -f1
# Exemplo de saída: em0, lo0

Entendendo as Jails FreeBSD — fundamentos e arquitetura de isolamento

Antes de partirmos para a prática, é crucial compreender a arquitetura que torna as Jails FreeBSD tão seguras. Uma jail não é simplesmente um chroot glorificado. O chroot (change root) apenas altera o diretório raiz aparente de um processo, mas não impede que um processo com privilégios de root escape desse ambiente — existem técnicas bem documentadas para burlar o chroot. A jail, por outro lado, foi projetada por Poul-Henning Kamp e introduzida no FreeBSD 4.0 com o objetivo explícito de criar um ambiente verdadeiramente isolado, onde nem mesmo o superusuário dentro da jail pode afetar o sistema host ou outras jails.

O isolamento das Jails FreeBSD opera em múltiplas camadas. A primeira é o isolamento do namespace de processos: processos dentro de uma jail só podem ver e interagir com outros processos que pertencem à mesma jail. Um ps aux executado dentro da jail mostrará apenas seus próprios processos, nunca os do host ou de outras jails. A segunda camada é o isolamento do sistema de arquivos: cada jail possui sua própria árvore de diretórios raiz, definida pelo parâmetro path. A terceira camada — e talvez a mais importante — é o isolamento de rede: uma jail pode ter seu próprio endereço IP, vinculado a uma interface de rede virtual ou alias, e não pode capturar tráfego destinado a outros IPs.

A quarta camada envolve restrições de privilégios. O FreeBSD mantém uma lista de chamadas de sistema (syscalls) e capacidades que são simplesmente desabilitadas dentro de jails por padrão. Por exemplo, um processo dentro da jail não pode carregar módulos do kernel (kldload), modificar parâmetros do sysctl que afetem o sistema global, criar dispositivos de rede ou alterar regras de firewall. Essas restrições são aplicadas pelo próprio kernel e não podem ser contornadas — não importa se o usuário dentro da jail é root, ele simplesmente não tem essas capacidades. Esse modelo é conhecido como root desprivilegiado ou “root jailado”.

Existem também parâmetros de segurança adicionais que podem ser habilitados, como enforce_statfs (que limita as informações visíveis sobre o sistema de arquivos), allow.mount (que pode ou não permitir montagens dentro da jail) e allow.raw_sockets (que controla o acesso a raw sockets, essenciais para ferramentas como ping e tcpdump). Compreender essas camadas é fundamental para configurar jails que sejam ao mesmo tempo funcionais para o propósito desejado e seguras contra escalonamento de privilégios. Em implantações profissionais na JRT Technology Solutions, sempre auditamos cada parâmetro allow individualmente, seguindo o princípio do menor privilégio.

Preparação do sistema host — datasets ZFS e ponte de rede (bridge)

O primeiro passo prático é preparar o sistema host para receber as jails. Vamos criar uma estrutura de datasets ZFS dedicada e configurar uma interface de rede do tipo bridge (ponte) para que as jails possam se comunicar com a rede externa como se fossem máquinas independentes. Utilizar datasets ZFS separados para as jails oferece vantagens enormes: snapshots instantâneos, clonagem eficiente com copy-on-write, compressão transparente e quotas de espaço. Execute todos os comandos a seguir como root no sistema host.

Vamos criar uma hierarquia de datasets sob zroot/jails. O dataset zroot/jails/base conterá uma instalação limpa do FreeBSD que servirá como template para todas as jails futuras — isso evita ter que baixar os sets de instalação repetidamente. O dataset zroot/jails/containers armazenará cada jail individual como um sub-dataset, permitindo gerenciamento granular.

# Criar dataset pai para todas as jails
zfs create -o mountpoint=/jails zroot/jails

# Criar dataset para a jail base (template)
zfs create -o mountpoint=/jails/base zroot/jails/base

# Criar dataset para os containers individuais
zfs create -o mountpoint=/jails/containers zroot/jails/containers

# Verificar a estrutura criada
zfs list -r zroot/jails
NAME                      USED  AVAIL  REFER  MOUNTPOINT
zroot/jails               176K  45.2G    96K  /jails
zroot/jails/base           96K  45.2G    96K  /jails/base
zroot/jails/containers     96K  45.2G    96K  /jails/containers

Agora vamos configurar a rede. O método recomendado é criar uma interface bridge (ponte) que conecta a interface física do host a interfaces virtuais epair atribuídas a cada jail. Isso permite que as jails obtenham endereços IP da mesma rede física que o host, como se fossem máquinas reais plugadas no mesmo switch. Alternativamente, pode-se usar aliases de IP na interface física, mas a bridge é mais flexível e profissional. Se seu host usa DHCP, você pode optar por NAT com pf, mas para esta aula focaremos no cenário de bridge com IPs estáticos — o mais comum em servidores.

Identifique sua interface de rede física. No exemplo abaixo, usamos em0 como interface física e atribuiremos o IP 192.168.1.100/24 ao host, enquanto a jail receberá 192.168.1.101/24. Ajuste os valores conforme sua rede real.

# Identificar interface física (substitua em0 pela sua)
ifconfig

# Criar a interface bridge (chamada bridge0)
ifconfig bridge create
# A saída será algo como: bridge0

# Adicionar a interface física como membro da bridge
ifconfig bridge0 addm em0

# Remover o IP da interface física (será movido para a bridge)
ifconfig em0 inet 192.168.1.100/24 delete

# Atribuir o IP à bridge
ifconfig bridge0 inet 192.168.1.100/24

# Subir a bridge
ifconfig bridge0 up

# Verificar configuração
ifconfig bridge0
bridge0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
        ether 58:9c:fc:10:ff:c7
        inet 192.168.1.100 netmask 0xffffff00 broadcast 192.168.1.255
        member: em0 flags=143<LEARNING,DISCOVER,AUTOEDGE,AUTOPTP>
        groups: bridge
        nd6 options=1<PERFORMNUD>

Para tornar essa configuração permanente, edite o arquivo /etc/rc.conf e adicione as linhas apropriadas. O exemplo abaixo assume que sua interface física é em0, que o host terá IP 192.168.1.100/24 e gateway 192.168.1.1. Ajuste conforme sua rede.

# Editar /etc/rc.conf para configuração permanente da bridge
# IMPORTANTE: adapte os IPs e interface para sua rede!

# Configuração da interface física (sem IP, apenas UP)
ifconfig_em0="up"

# Configuração da bridge
cloned_interfaces="bridge0"
ifconfig_bridge0="inet 192.168.1.100/24 addm em0 up"

# Gateway padrão (se aplicável)
defaultrouter="192.168.1.1"

# Habilitar forwarding de pacotes entre interfaces (opcional, mas recomendado)
gateway_enable="YES"

Após editar o arquivo, reinicie o serviço de rede para aplicar as mudanças sem reboot completo:

service netif restart && service routing restart

Criando a jail base — download e instalação do sistema FreeBSD no template

Com o sistema host preparado, vamos criar uma jail base que servirá como template limpo para todas as jails futuras. Este template conterá uma instalação mínima do FreeBSD (base system) sem kernels desnecessários, sem ports e sem código fonte — apenas o essencial para um ambiente funcional. Utilizaremos o comando bsdinstall no modo de extração automatizada, que baixa e instala as distribuições oficiais do FreeBSD diretamente no diretório alvo.

Criaremos um script que baixa os sets base.txz e kernel.txz (apenas o necessário) da versão exata do FreeBSD que está rodando no host. Isso garante compatibilidade binária total, já que host e jails compartilham o mesmo kernel. O diretório alvo será /jails/base/14.0-RELEASE, permitindo manter múltiplas versões de template se necessário.

# Definir a versão do FreeBSD em execução
export VERSION=$(freebsd-version -k | cut -d- -f1)
echo "Versão detectada: $VERSION"

# Criar diretório para o template
mkdir -p /jails/base/${VERSION}

# Baixar os sets de instalação do mirror oficial
fetch -o /tmp/base.txz http://ftp.freebsd.org/pub/FreeBSD/releases/amd64/${VERSION}/base.txz
fetch -o /tmp/kernel.txz http://ftp.freebsd.org/pub/FreeBSD/releases/amd64/${VERSION}/kernel.txz

# Extrair os sets para o diretório do template
tar -xpf /tmp/base.txz -C /jails/base/${VERSION}
tar -xpf /tmp/kernel.txz -C /jails/base/${VERSION}

# Remover os arquivos baixados para liberar espaço
rm /tmp/base.txz /tmp/kernel.txz
Versão detectada: 14.0-RELEASE
/tmp/base.txz                                   100% of  178 MB  12 MBps 00m15s
/tmp/kernel.txz                                 100% of   47 MB  11 MBps 00m05s

Agora precisamos configurar alguns arquivos essenciais dentro do template para que a jail funcione corretamente. Vamos criar um /etc/resolv.conf básico para resolução DNS e um /etc/rc.conf mínimo que evite a inicialização de serviços desnecessários dentro da jail.

# Configurar DNS dentro do template
cat > /jails/base/${VERSION}/etc/resolv.conf < /jails/base/${VERSION}/etc/rc.conf <

Este template está pronto para ser usado como base para a criação de jails individuais. A beleza do ZFS entra em cena agora: em vez de copiar todos os arquivos para cada nova jail, criaremos um snapshot do dataset base e, em seguida, um clone para cada jail. O ZFS utiliza copy-on-write, então o espaço ocupado inicialmente é quase zero — apenas metadados. Conforme cada jail modifica arquivos, novos blocos são alocados individualmente.

Configuração e inicialização de jails com /etc/jail.conf

O método clássico e mais direto para gerenciar Jails FreeBSD é através do arquivo /etc/jail.conf. Este arquivo define todas as jails do sistema com seus parâmetros individuais, e o serviço jail do rc.d se encarrega de iniciá-las e pará-las automaticamente durante o boot e shutdown. Vamos criar uma jail chamada webserver baseada no template que acabamos de preparar. Primeiro, criamos um snapshot do dataset base e um clone ZFS para a nova jail.

# Criar snapshot somente leitura do template
zfs snapshot zroot/jails/base/${VERSION}@template

# Criar clone para a jail webserver
zfs clone -o mountpoint=/jails/containers/webserver \
    zroot/jails/base/${VERSION}@template \
    zroot/jails/containers/webserver

# Verificar o clone
zfs list -r zroot/jails/containers
NAME                               USED  AVAIL  REFER  MOUNTPOINT
zroot/jails/containers             216K  45.2G    96K  /jails/containers
zroot/jails/containers/webserver    24K  45.2G   532M  /jails/containers/webserver

Note que o dataset clone ocupa apenas 24K inicialmente, mesmo referenciando 532M de dados do template. Isso é a mágica do copy-on-write do ZFS. Agora vamos criar o arquivo /etc/jail.conf com a definição da nossa jail. Cada parâmetro será explicado linha a linha para que você entenda exatamente o que está sendo configurado e por quê.

# /etc/jail.conf — Definição completa da jail "webserver"
# Este arquivo controla todas as jails do sistema

# Parâmetros globais (aplicados a todas as jails, podem ser sobrescritos)
exec.start = "/bin/sh /etc/rc";      # Comando para iniciar a jail
exec.stop = "/bin/sh /etc/rc.shutdown"; # Comando para parar a jail
exec.clean;                          # Limpa o ambiente antes de executar comandos
mount.devfs;                         # Monta /dev filesystem dentro da jail

# Definição da jail "webserver"
webserver {
    # Interface e IP: a jail "enxerga" apenas este IP
    ip4.addr = "192.168.1.101";
    
    # Caminho no host para o sistema de arquivos da jail
    path = "/jails/containers/webserver";
    
    # Hostname que a jail reportará
    host.hostname = "webserver.jail.local";
    
    # Restrições de segurança (hardening)
    enforce_statfs = "1";    # Restringe informações do statfs/df
    allow.raw_sockets = "0"; # Desabilita raw sockets (ping não funciona)
    allow.mount = "0";       # Impede montagens dentro da jail
    allow.set_hostname = "0";# Impede mudança de hostname
    allow.sysvipc = "0";     # Desabilita System V IPC (semaphores, msg queues)
    
    # Limites de recursos (opcional mas recomendado)
    # cpuset.id = "0";       # Vincular a CPU específica (descomente se desejar)
    # memory.use = "512M";   # Limitar memória (requer RACCT habilitado)
}

Vamos entender cada parâmetro crítico de segurança que configuramos. enforce_statfs="1" faz com que comandos como df e mount dentro da jail mostrem apenas informações do sistema de arquivos da própria jail, ocultando detalhes sobre o host. allow.raw_sockets="0" impede a criação de sockets RAW, necessários para ferramentas como ping, tcpdump e certos tipos de scanners de rede — isso dificulta ataques de sniffing a partir de uma jail comprometida. allow.mount="0" bloqueia completamente a capacidade de montar sistemas de arquivos, incluindo tmpfs e nullfs. allow.set_hostname="0" impede que o root da jail altere o hostname, útil para logging e auditoria. allow.sysvipc="0" desabilita mecanismos IPC do System V (semáforos, filas de mensagens, memória compartilhada), que raramente são necessários e podem ser vetores de ataque.

Com o arquivo configurado, vamos iniciar a jail e verificar seu funcionamento. O serviço jail do rc.d gerencia todas as jails definidas em /etc/jail.conf.

# Habilitar o serviço jail no boot
sysrc jail_enable="YES"

# Iniciar a jail webserver manualmente
service jail start webserver

# Listar todas as jails em execução
jls
Starting jails: webserver.
JID  IP Address      Hostname               Path
1    192.168.1.101   webserver.jail.local   /jails/containers/webserver

A saída do jls confirma que a jail está rodando com JID (Jail ID) 1, o IP que atribuímos, o hostname configurado e o caminho do sistema de arquivos. Agora vamos executar um shell dentro da jail para interagir com ela e verificar o isolamento.

# Executar um shell interativo dentro da jail
jexec webserver /bin/sh

# Agora DENTRO da jail, execute:
hostname
ifconfig
ps aux
df -h
exit
# hostname
webserver.jail.local

# ifconfig
em0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
        options=4800028<VLAN_MTU,JUMBO_MTU,NOMAP>
        inet 192.168.1.101 netmask 0xffffff00 broadcast 192.168.1.255

# ps aux
USER   PID %CPU %MEM   VSZ   RSS TT  STAT STARTED    TIME COMMAND
root  1489  0.0  0.1 12780  2620  -  SsJ  14:22   0:00.01 /bin/sh /etc/rc
root  1495  0.0  0.1 12844  2720  -  SsJ  14:22   0:00.01 /usr/sbin/syslogd -ss

# df -h
Filesystem                     Size    Used   Avail Capacity  Mounted on
zroot/jails/containers/webserver  45G    532M     45G     1%    /
devfs                          1.0K    1.0K      0B   100%    /dev

Observe que o comando ps aux mostra apenas dois processos: o script de inicialização e o syslogd. Nenhum processo do host aparece. O df -h mostra apenas o sistema de arquivos da jail e o devfs, sem informações sobre outros datasets do host. O ifconfig mostra apenas a interface com o IP da jail — mesmo que o host tenha múltiplas interfaces, a jail só "enxerga" aquela à qual foi vinculada. Isso demonstra o isolamento em ação.

Gerenciamento moderno de Jails FreeBSD com iocage

Embora o jail.conf seja perfeitamente funcional e adequado para cenários estáticos, o gerenciamento de múltiplas jails em ambientes dinâmicos pede ferramentas mais flexíveis. O iocage é um gerenciador de jails escrito em Python que se integra profundamente com ZFS, oferecendo operações como criação, clonagem, snapshot, rollback, exportação e importação de jails com comandos intuitivos. Em nossos projetos na JRT Technology Solutions, padronizamos o iocage para todos os deploys novos, reservando o jail.conf para cenários legados ou extremamente simples.

O iocage trabalha com o conceito de releases (versões do FreeBSD disponíveis para download automático) e templates (jails base personalizadas). Ele gerencia automaticamente a rede, podendo criar jails em modo VNET (cada jail com sua própria pilha de rede virtual completa) ou shared IP (compartilhando a pilha de rede do host). O VNET é o modo recomendado para isolamento máximo. Vamos instalar o iocage e criar uma jail de exemplo.

# Instalar o iocage usando o gerenciador de pacotes oficial
pkg install -y iocage

# Habilitar o serviço iocage no boot
sysrc iocage_enable="YES"

# Inicializar o iocage (isso criará um dataset ZFS dedicado)
iocage activate zroot
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 1 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
        iocage: 1.7_3

Number of packages to be installed: 1
...
iocage activated on zroot/iocage

O iocage cria automaticamente um dataset em zroot/iocage com toda a estrutura necessária: sub-datasets para downloads, releases, jails, templates e snapshots. Agora vamos baixar uma release do FreeBSD usando o próprio iocage — ele buscará os sets oficiais e os extrairá em um dataset otimizado. Isso substitui o processo manual que fizemos anteriormente com fetch e tar.

# Listar releases disponíveis para download (requer Internet)
iocage fetch

# Para baixar uma release específica, use:
# iocage fetch release=14.0-RELEASE

# O download e extração podem levar alguns minutos
# Acompanhe o progresso no terminal
Fetching: 14.0-RELEASE
Downloading base...
Downloading kernel...
Downloading lib32...
Extracting: base.txz...
Extracting: kernel.txz...
Extracting: lib32.txz...
Release 14.0-RELEASE successfully fetched!

Com a release baixada, criar uma nova jail é questão de um único comando. Vamos criar uma jail chamada appserver com interface VNET, que terá sua própria pilha de rede virtual isolada. O iocage criará automaticamente um par de interfaces epair (uma ponta na jail, outra na bridge do host) e configurará o IP.

# Criar jail com VNET e IP estático
iocage create -n appserver \
    release="14.0-RELEASE" \
    ip4_addr="vnet0|192.168.1.102/24" \
    defaultrouter="192.168.1.1" \
    host_hostname="appserver.jail.local" \
    boot="on"

# Listar todas as jails gerenciadas pelo iocage
iocage list
+-----+-----------+-------+--------------+---------------------+
| JID |   NAME    | STATE |   RELEASE    |         IP4         |
+=====+===========+=======+==============+=====================+
| 2   | appserver | up    | 14.0-RELEASE | vnet0|192.168.1.102 |
+-----+-----------+-------+--------------+---------------------+

O parâmetro boot="on" instrui o iocage a iniciar automaticamente esta jail durante o boot do sistema. A interface vnet0 é um nome lógico dentro da jail — externamente, o iocage gerencia o par epair e o conecta à bridge que configuramos. Vamos verificar a conectividade de rede e executar um comando dentro da nova jail.

# Executar comando dentro da jail appserver para verificar rede
iocage exec appserver "ifconfig"

# Testar conectividade com o gateway
iocage exec appserver "ping -c 3 192.168.1.1"

# Acessar shell interativo na jail
iocage console appserver
# Dentro da jail, ifconfig mostra:
lo0: flags=1008049<UP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP> ...
        inet 127.0.0.1 netmask 0xff000000
vnet0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> ...
        inet 192.168.1.102 netmask 0xffffff00 broadcast 192.168.1.255

# ping -c 3 192.168.1.1
PING 192.168.1.1 (192.168.1.1): 56 data bytes
64 bytes from 192.168.1.1: icmp_seq=0 ttl=64 time=0.580 ms
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.405 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.398 ms

O iocage oferece ainda comandos poderosos para gerenciamento do ciclo de vida das jails. Abaixo, uma tabela de referência rápida com os comandos mais utilizados no dia a dia.

Tabela 1: Comandos essenciais do iocage para gerenciamento de Jails FreeBSD
Comando Descrição Exemplo de uso
iocage create -n <nome> Cria uma nova jail com o nome especificado iocage create -n webserver release="14.0-RELEASE" ip4_addr="vnet0|192.168.1.101/24"
iocage list Lista todas as jails e seus estados iocage list
iocage start <nome> Inicia uma jail parada iocage start webserver
iocage stop <nome> Para uma jail em execução iocage stop webserver
iocage restart <nome> Reinicia uma jail iocage restart webserver
iocage destroy -f <nome> Remove permanentemente uma jail e seu dataset iocage destroy -f webserver
iocage exec <nome> "<comando>" Executa um comando dentro da jail sem abrir shell interativo iocage exec webserver "pkg update"
iocage console <nome> Abre um shell interativo (login) na jail iocage console webserver
iocage snapshot <nome> -n <snapname> Cria um snapshot ZFS da jail iocage snapshot webserver -n antes-upgrade
iocage rollback <nome> -n <snapname> Restaura a jail para um snapshot anterior iocage rollback webserver -n antes-upgrade
iocage clone <origem> -n <destino> Clona uma jail existente iocage clone webserver -n webserver-dev
iocage set <propriedade=valor> <nome> Altera propriedades de uma jail iocage set host_hostname="novohost" webserver

Verificando a Instalação / Testando a Configuração

Após criar e iniciar suas jails, é essencial realizar uma bateria de verificações para garantir que tudo está funcionando conforme esperado — tanto o isolamento quanto a conectividade. Esta seção fornece um roteiro completo de testes que utilizamos rotineiramente na JRT Technology Solutions para validar ambientes de produção. Execute cada bloco e compare a saída obtida com a saída esperada documentada abaixo.

O primeiro teste verifica se o serviço de jails está rodando e reconhece todas as jails configuradas. Tanto jails do jail.conf quanto do iocage aparecerão no jls, pois ambas utilizam a mesma infraestrutura do kernel.

# Verificar TODAS as jails em execução (comandos nativos)
jls -v

# Verificar jails gerenciadas pelo iocage especificamente
iocage list -l

# Verificar processos dentro de uma jail específica
ps -J webserver -aux
# jls -v
JID  Hostname               IP Address(es)          Path
1    webserver.jail.local   192.168.1.101           /jails/containers/webserver
2    appserver.jail.local   192.168.1.102           /zroot/iocage/jails/appserver/root

# ps -J webserver -aux (executado no host, mostra processos da jail)
USER   PID %CPU %MEM   VSZ   RSS TT  STAT STARTED    TIME COMMAND
root  1489  0.0  0.1 12780  2620  -  SsJ  14:22   0:00.01 /bin/sh /etc/rc
root  1495  0.0  0.1 12844  2720  -  SsJ  14:22   0:00.01 /usr/sbin/syslogd -ss

O segundo teste foca na conectividade de rede. Vamos verificar se a jail consegue alcançar o gateway, resolver nomes DNS e acessar a Internet. Também testaremos o isolamento reverso: o host NÃO deve conseguir acessar serviços que rodam exclusivamente na jail por engano — a menos que explicitamente configurado.

# Testar conectividade da jail webserver
jexec webserver ping -c 3 8.8.8.8

# Testar resolução DNS dentro da jail
jexec webserver host freebsd.org

# Testar conectividade da jail appserver (via iocage)
iocage exec appserver "ping -c 3 freebsd.org"

# Verificar se o host "enxerga" o IP da jail (deve responder se bridge OK)
ping -c 3 192.168.1.101
# jexec webserver ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=118 time=12.3 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=11.8 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=12.1 ms

# jexec webserver host freebsd.org
freebsd.org has address 96.47.72.84
freebsd.org mail is handled by 10 mx1.freebsd.org.

# ping -c 3 192.168.1.101 (do host para a jail)
PING 192.168.1.101 (192.168.1.101): 56 data bytes
64 bytes from 192.168.1.101: icmp_seq=0 ttl=64 time=0.120 ms
64 bytes from 192.168.1.101: icmp_seq=1 ttl=64 time=0.098 ms

O terceiro teste verifica as restrições de segurança. Tentaremos executar ações que deveriam ser bloqueadas pelas políticas de hardening que configuramos.

# Tentar carregar um módulo do kernel (DEVE FALHAR)
jexec webserver kldload tmpfs

# Tentar acessar raw sockets com ping da jail webserver
# Lembre-se: configuramos allow.raw_sockets=0 para esta jail
jexec webserver ping -c 1 192.168.1.1

# Tentar ver processos do host de dentro da jail
jexec webserver ps aux
# jexec webserver kldload tmpfs
kldload: can't load tmpfs: Operation not permitted

# jexec webserver ping -c 1 192.168.1.1
ping: ssend socket: Operation not permitted

# jexec webserver ps aux (mostra apenas processos da própria jail)
USER   PID %CPU %MEM   VSZ   RSS TT  STAT STARTED    TIME COMMAND
root  1489  0.0  0.1 12780  2620  -  SsJ  14:22   0:00.01 /bin/sh /etc/rc
root  1495  0.0  0.1 12844  2720  -  SsJ  14:22   0:00.01 /usr/sbin/syslogd -ss

Estas saídas confirmam que as restrições estão ativas: o kernel impede a carga de módulos, o acesso a raw sockets é negado (ping falha com "Operation not permitted") e o isolamento de processos funciona corretamente. Para a jail appserver (criada com iocage e permissões padrão mais permissivas), o ping pode funcionar — depende das políticas padrão do iocage para a release utilizada.

Erros Comuns e Como Resolver

Durante a configuração e operação de Jails FreeBSD, alguns erros são recorrentes, especialmente para administradores que estão migrando de outras plataformas ou iniciando no FreeBSD. Abaixo, compilamos os quatro problemas mais frequentes que encontramos em campo, com a causa raiz detalhada, os sintomas que você observará e a solução completa passo a passo.

  • Erro 1: "Cannot start jail: ifconfig: interface bridge0 does not exist"
    Sintoma: Ao tentar iniciar a jail, o comando falha imediatamente com uma mensagem indicando que a interface bridge0 não existe.
    Causa: A interface bridge não foi criada antes de tentar iniciar a jail, ou não foi configurada para ser criada automaticamente no boot através do /etc/rc.conf. A jail tenta vincular seu IP a uma interface que o kernel desconhece.
    Solução: Execute ifconfig bridge create para criar a bridge manualmente. Em seguida, verifique se as linhas cloned_interfaces="bridge0" e ifconfig_bridge0="..." estão corretamente definidas no /etc/rc.conf. Certifique-se de que a interface física listada no addm realmente existe no sistema (use ifconfig -l para listar todas as interfaces). Por fim, reinicie o netif com service netif restart.
  • Erro 2: "jail: ip4.addr: Address already in use" ou "bind: Can't assign requested address"
    Sintoma: O serviço jail falha ao iniciar, reportando que o endereço IP especificado já está em uso no sistema. Isso pode ocorrer mesmo que você não tenha configurado explicitamente aquele IP em nenhuma interface.
    Causa: O endereço IP que você está tentando atribuir à jail já está vinculado a uma interface do host (incluindo loopback ou aliases) ou já está sendo usado por outra jail em execução. Cada IP deve ser único no escopo do host.
    Solução: Liste todos os IPs configurados no sistema com ifconfig | grep "inet ". Se o IP desejado aparecer em alguma interface (incluindo lo0), remova-o ou escolha outro IP livre. Lembre-se de que o IP da jail não pode ser o mesmo IP do host na mesma interface. Se você está usando bridge com epair/VNET (iocage), o IP da jail é vinculado à interface virtual vnet0 dentro da jail, não à bridge do host — mas ainda assim não pode conflitar com nenhum IP do host.
  • Erro 3: "Exec format error" ou "Shared object has no run-time symbol table" ao executar binários na jail
    Sintoma: Ao tentar executar comandos dentro da jail (incluindo /bin/sh), você recebe erros de formato de executável ou bibliotecas incompatíveis. A jail inicia mas nenhum comando funciona.
    Causa: Incompatibilidade entre a arquitetura ou versão do sistema base da jail e o kernel do host. O caso mais comum é ter baixado sets para a arquitetura errada (ex: i386 em host

Quer aprender na prática com especialistas?

A JRT Technology Solutions oferece treinamentos e implementação de FreeBSD para equipes corporativas.



Falar no WhatsApp

Thiago Paes Rodrigues

Com mais de 22 anos de experiência em Tecnologia da Informação, este profissional construiu uma trajetória sólida como empresário, atuando de forma estratégica na implementação de soluções tecnológicas que otimizam processos e impulsionam resultados em diferentes setores.