Domine o `chroot`: Guia prático para criar ambientes Linux isolados

O comando `chroot` pode ser uma ferramenta poderosa para isolar ambientes de desenvolvimento ou teste, além de reforçar a segurança do seu sistema. Vamos explorar uma maneira simples de utilizá-lo.

O que realmente é `chroot`?

A verdadeira utilidade de um comando reside em sua funcionalidade e facilidade de uso. Um comando complexo ou de difícil configuração pode acabar sendo pouco utilizado, mesmo que ofereça recursos valiosos. Em discussões com usuários de Linux, o `chroot` frequentemente é visto como difícil de usar, complexo e demorado de configurar, o que limita seu uso, apesar de seu potencial.

Com `chroot`, você pode configurar e executar programas ou shells interativos, como o Bash, em um sistema de arquivos isolado. Este ambiente impedido de interagir diretamente com seu sistema de arquivos regular. Dentro desse ambiente, tudo é contido, e nenhum processo pode acessar diretórios fora de sua raiz designada sem privilégios de root. Por essa característica, ambientes `chroot` são frequentemente chamados de “jaulas”. É importante não confundir o termo “prisão” com o comando `jail` do FreeBSD, que cria um ambiente chroot com segurança reforçada.

Felizmente, existe uma abordagem simples para usar `chroot`, que abordaremos neste artigo. Usaremos comandos Linux básicos, compatíveis com todas as distribuições. Algumas distribuições oferecem ferramentas específicas para facilitar a criação de ambientes `chroot`, como o debootstrap no Ubuntu, mas aqui focaremos em uma solução independente da distribuição.

Quando usar `chroot`?

Um ambiente `chroot` oferece funcionalidades similares a uma máquina virtual, mas de forma mais leve. Ele não requer um hipervisor como o VirtualBox ou o Virtual Machine Manager, nem um kernel próprio, pois compartilha o kernel do sistema hospedeiro.

Ambientes `chroot` são mais parecidos com contêineres como LXC do que com máquinas virtuais. São rápidos para configurar e podem ser automatizados. Assim como os contêineres, uma prática comum é instalar apenas o necessário para a tarefa desejada, definindo um ambiente mínimo e eficiente. A necessidade específica de cada ambiente vai depender de como ele será utilizado.

Algumas aplicações comuns incluem:

  • Desenvolvimento e teste de software: Desenvolvedores e equipes de controle de qualidade (CQ) podem usar `chroot` para criar ambientes isolados com as dependências mínimas necessárias, garantindo que o software funcione corretamente em diferentes condições. Isso evita problemas causados por dependências presentes apenas no ambiente do desenvolvedor.
  • Ambiente de desenvolvimento seguro: Desenvolvedores podem criar ambientes `chroot` dedicados para não comprometer o sistema principal com experimentos e testes.
  • Execução de software antigo: Ambientes `chroot` permitem rodar softwares que exigem versões antigas do sistema operacional sem conflitos com o sistema atual.
  • Recuperação e atualização de sistemas: Se um sistema Linux se tornar inoperável, o `chroot` pode ser usado para montar o sistema de arquivos danificado a partir de um Live CD, permitindo reparos como se o sistema estivesse montado normalmente no diretório raiz (`/`). Esta técnica foi usada em um artigo anterior sobre migração de sistemas de arquivos de ext2 ou ext3 para ext4.
  • Isolamento de aplicações: Executar servidores como FTP em ambientes `chroot` limita os danos causados por possíveis invasões, adicionando uma camada de segurança ao sistema.

Criando um ambiente `chroot`

Primeiro, precisamos de um diretório que servirá como a raiz do nosso ambiente `chroot`. Para facilitar a referência a este diretório, vamos definir uma variável para armazenar seu caminho. Neste exemplo, vamos usar o diretório “testroot”, que será criado posteriormente. O diretório precisa estar vazio ou ser inexistente.

chr=/home/dave/testroot

Caso o diretório não exista, podemos criá-lo com o seguinte comando. A opção `-p` garante que todos os diretórios pais sejam criados automaticamente:

mkdir -p $chr

Agora, vamos criar os diretórios necessários para hospedar as partes do sistema operacional que nosso ambiente `chroot` irá utilizar. Criaremos um ambiente Linux minimalista com Bash como shell interativo, e incluiremos os comandos `touch`, `rm` e `ls`. Isso nos permitirá criar, listar e remover arquivos, além de usar o Bash. Para este exemplo, isso será suficiente.

Utilizaremos a expansão de chaves para listar os diretórios que precisamos criar:

mkdir -p $chr/{bin,lib,lib64}

Em seguida, vamos mudar o diretório de trabalho para a nova raiz:

cd $chr

Agora, copiaremos os binários necessários para nosso ambiente Linux minimalista de seu diretório `/bin` padrão para nosso diretório `chroot`/bin`. A opção `-v` (verbose) exibe o que está sendo copiado em cada etapa:

cp -v /bin/{bash,touch,ls,rm} $chr

Os arquivos são copiados:

Esses binários possuem dependências que também precisamos copiar. Caso contrário, eles não funcionarão dentro do ambiente `chroot`. Para descobrir essas dependências, usaremos o comando `ldd`. Vamos começar com o `bash`.

ldd /bin/bash

As dependências são listadas no terminal:

Precisamos copiar todas as dependências para o novo ambiente. Copiá-las uma a uma seria demorado e propenso a erros. Para facilitar, vamos automatizar um pouco o processo.

Vamos listar as dependências novamente, mas dessa vez, vamos formar uma lista. Em seguida, vamos percorrer a lista, copiando os arquivos.

Aqui, estamos usando `ldd` para listar as dependências e encaminhando a saída para o `egrep`. `egrep` é equivalente ao `grep -E` (expressões regulares estendidas). A opção `-o` (apenas correspondência) restringe a saída às partes correspondentes das linhas. Estamos buscando arquivos de biblioteca que terminam em um número.

list="$(ldd /bin/bash | egrep -o '/lib.*.[0-9]')"

Podemos verificar o conteúdo da variável `list` usando `echo`:

echo $list

Agora que temos a lista, podemos percorrê-la usando o seguinte loop. Para cada elemento da lista, copiamos o arquivo para o diretório raiz `chroot`, que é o valor armazenado em `$chr`. A opção `-v` exibe cada cópia realizada e a opção `–parents` garante que todos os diretórios pais sejam criados automaticamente no ambiente `chroot`.

for i in $list; do cp -v --parents "$i" "${chr}"; done

E esta é a saída:

Usaremos a mesma técnica para capturar as dependências dos outros comandos. Precisaremos apenas modificar o comando que coleta as dependências.

Podemos recuperar o comando do histórico e editá-lo. O loop de cópia não precisa ser alterado.

Aqui, recuperamos o comando e o editamos para usar `touch` em vez de `bash`.

list="$(ldd /bin/touch | egrep -o '/lib.*.[0-9]')"

Agora, repetimos o loop de cópia:

for i in $list; do cp -v --parents "$i" "${chr}"; done

E os arquivos são copiados:

Agora, editamos a linha de comando da lista para `ls`:

list="$(ldd /bin/ls | egrep -o '/lib.*.[0-9]')"

Novamente, vamos usar o mesmo loop de cópia. Não importa quais arquivos estão na lista, o loop irá copiar todos eles.

for i in $list; do cp -v --parents "$i" "${chr}"; done

E as dependências de `ls` são copiadas:

Finalmente, editamos a linha de comando para funcionar com `rm`:

list="$(ldd /bin/ls | egrep -o '/lib.*.[0-9]')"

Usamos o loop de cópia pela última vez:

for i in $list; do cp -v --parents "$i" "${chr}"; done

As últimas dependências são copiadas para nosso ambiente `chroot`. Agora, estamos prontos para usar o comando `chroot`. Este comando define a raiz do ambiente e especifica qual aplicativo deve ser executado como o shell.

sudo chroot $chr /bin/bash

Nosso ambiente `chroot` está ativo. O prompt mudou, e o shell interativo está sendo gerenciado pelo `bash` dentro do ambiente.

Podemos testar os comandos que adicionamos ao ambiente.

ls
ls /home/dave/Documents

O comando `ls` funciona como esperado dentro do ambiente. Quando tentamos acessar um diretório fora do ambiente, o comando falha.

Podemos usar `touch` para criar um arquivo, `ls` para listá-lo e `rm` para removê-lo.

touch sample_file.txt
ls
rm sample_file.txt
ls

Também podemos usar os comandos embutidos do Bash. Digite `help` na linha de comando para ver a lista.

help

Para sair do ambiente `chroot`, use `exit`:

exit

Se você deseja remover o ambiente `chroot`, basta excluí-lo:

rm -r testroot/

Isso irá excluir recursivamente todos os arquivos e diretórios dentro do ambiente `chroot`.

Automatizando para maior conveniência

Se você considera os ambientes `chroot` úteis, mas a configuração é um pouco trabalhosa, lembre-se que você pode automatizar tarefas repetitivas usando aliases, funções e scripts, economizando tempo e evitando erros.