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.