Como executar chroot com namespaces do Linux?

14

Depois de ler sobre os namespaces do Linux, tive a impressão de que eles são, entre muitos outros recursos, uma alternativa ao chroot. Por exemplo, neste artigo :

Outros usos [de espaços para nome] incluem [...] isolamento ao estilo chroot () de um processo em uma parte da hierarquia de diretório único.

No entanto, quando clono o espaço para nome da montagem, por exemplo, com o seguinte comando, ainda vejo toda a árvore raiz original.

unshare --mount -- /bin/bash

Entendo que agora posso realizar montagens adicionais no novo espaço para nome que não são compartilhadas com o espaço para nome original e, portanto, isso fornece isolamento, mas ainda é a mesma raiz, por exemplo, /etcainda é a mesma para os dois espaços para nome. Ainda preciso chrootalterar a raiz ou existe uma alternativa?

Eu esperava que essa pergunta pudesse fornecer uma resposta, mas a resposta só usa chrootnovamente.

EDIT # 1

Agora, havia um comentário excluído mencionado pivot_root. Como isso realmente faz parte linux/fs/namespace.c, na verdade faz parte da implementação dos namespaces. Isso sugere que alterar o diretório raiz apenas com unsharee mountnão é possível, mas os namespaces fornecem uma versão própria - mais inteligente - do chroot. Ainda não entendi a idéia principal dessa abordagem que a torna fundamentalmente diferente chroot, mesmo depois de ler o código-fonte (no sentido de, por exemplo, segurança ou melhor isolamento).

EDIT # 2

Esta não é uma duplicata desta pergunta . Depois de executar todos os comandos da resposta, tenho /tmp/tmp.vyM9IwnKuY (ou similar), mas o diretório raiz ainda é o mesmo!

koalo
fonte
Com relação à diferença entre pivot_roote chroot: dei uma olhada nas fontes do Docker e descobri que, se ele falha na execução pivot_root, ele volta a chroot, ou seja, esses mecanismos são considerados pelo menos semelhantes em funcionalidade para fins de contêiner.
Danila Kiver

Respostas:

13

Inserir um espaço para nome de montagem antes de configurar um chroot, permite evitar o espaço para nome do host com montagens adicionais, por exemplo, para /proc. Você pode usar chrootdentro de um namespace mount como um hack simples e agradável.

Eu acho que há vantagens em entender pivot_root, mas tem uma curva de aprendizado. A documentação não explica tudo muito bem ... embora haja um exemplo de uso em man 8 pivot_root(para o comando shell). man 2 pivot_root(para a chamada do sistema) pode ser mais claro se fizer o mesmo e incluir um exemplo de programa C.

Como usar pivot_root

Imediatamente após inserir o espaço para nome da montagem, você também precisa mount --make-rslave /ou equivalente. Caso contrário, todas as suas alterações de montagem serão propagadas para as montagens no espaço para nome original, incluindo o pivot_root. Você não quer isso :).

Se você usou o unshare --mountcomando, observe que ele está documentado para ser aplicado mount --make-rprivatepor padrão. AFAICS, esse é um padrão ruim e você não deseja isso no código de produção. Por exemplo, nesse ponto, ele deixaria ejectde funcionar em um DVD ou USB montado no espaço para nome do host. O DVD ou USB permaneceria montado dentro da árvore de montagem privada e o kernel não permitiria ejetar o DVD.

Depois de fazer isso, você pode montar, por exemplo, o /procdiretório que você usará. Da mesma maneira que você faria chroot.

Diferentemente de quando você usa chroot, pivot_rootrequer que seu novo sistema de arquivos raiz seja um ponto de montagem. Se não for um já, você pode satisfazer esta simplesmente aplicando montar um ligamento: mount --rbind new_root new_root.

Use pivot_root- e, em seguida, umounto antigo sistema de arquivos raiz, com a opção -l/ MNT_DETACH. ( Você não precisa umount -R, o que pode levar mais tempo. ).

Tecnicamente, o uso pivot_rootgeralmente precisa envolver o uso chroottambém; não é "um ou outro".

Conforme man 2 pivot_rootdefinido, é definido apenas como a troca da raiz do espaço para nome da montagem. Não está definido para alterar para qual diretório físico a raiz do processo está apontando. Ou o diretório de trabalho atual ( /proc/self/cwd). Acontece que isso acontece , mas este é um truque para lidar com os threads do kernel. A página de manual diz que isso pode mudar no futuro.

Geralmente você deseja esta sequência:

chdir(new_root);            // cd new_root
pivot_root(".", put_old);   // pivot_root . put_old
chroot(".");                // chroot .

A publicação do chrootnesta sequência é mais um detalhe sutil . Embora o objetivo pivot_rootseja reorganizar o espaço para nome da montagem, o código do kernel parece encontrar o sistema de arquivos raiz movendo-se olhando a raiz por processo, que é o que chrootdefine.

Por que usar pivot_root

Em princípio, faz sentido usar pivot_rootpara segurança e isolamento. Eu gosto de pensar sobre a teoria da segurança baseada em capacidade . Você passa uma lista dos recursos específicos necessários e o processo não pode acessar outros recursos. Neste caso, estamos falando sobre os sistemas de arquivos passados ​​para um espaço para nome de montagem. Essa ideia se aplica geralmente ao recurso "namespaces" do Linux, embora provavelmente não esteja expressando muito bem.

chrootdefine apenas a raiz do processo, mas o processo ainda se refere ao namespace de montagem completa. Se um processo reter o privilégio de executar chroot, ele poderá percorrer o espaço de nome do sistema de arquivos. Conforme detalhado em man 2 chroot"o superusuário pode escapar de uma 'prisão chroot' até ...".

Outra maneira instigante de desfazer chrooté nsenter --mount=/proc/self/ns/mnt. Este é talvez um argumento mais forte para o princípio. nsenter/ setns()necessariamente recarrega a raiz do processo, a partir da raiz do namespace mount ... embora o fato de que isso funcione quando os dois se refiram a diretórios físicos diferentes, possa ser considerado um bug do kernel. (Nota técnica: pode haver vários sistemas de arquivos montados um sobre o outro na raiz; setns()usa o topo, mais recentemente montado).

Isso ilustra uma vantagem de combinar um espaço para nome de montagem com um "espaço para nome PID". Estar dentro de um espaço de nome PID impediria a inserção do espaço para nome de montagem de um processo não confinado. Também impede que você entre na raiz de um processo não confinado ( /proc/$PID/root). E, é claro, um espaço para nome PID também impede que você interrompa qualquer processo que esteja fora dele :-).

sourcejedi
fonte
Isso já ajuda muito. Ainda assim, não tenho certeza do que você quer dizer com "a montagem na parte superior do espaço para nome". E existe uma maneira de mudar isso?
21918 koalo
1
@koalo editado :-). ps Eu não sei por que você precisaria do fstab para "make-rslave" / "make-rprivate". O switch-root.c do systemd simplesmente funcionamount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL)
sourcejedi
1
O @koalo e os desenvolvedores do kernel do linux usaram "rootfs" quando nomearam uma quarta coisa :-P. unix.stackexchange.com/questions/152029/…
sourcejedi
1
Esta resposta e outras de @sourcejedi foram excepcionalmente úteis, eu teria perguntado "pivot_root: não é possível colocar o put_old tão ocupado", mas a resposta já está aqui, seja preguiçoso porque a força não funcionaráumount -l ./oldroot
earcam
1
Recentemente, houve uma atualização na página do manual pivot_root (2) com vários esclarecimentos, e agora inclui um programa de exemplo. Você pode atualizar sua resposta para refletir isso? A página de manual agora também explica o bom pivot_root(".", ".")truque, que é realmente a maneira mais fácil de usar pivot_rootna maioria das circunstâncias (não é chrootnecessário).
Philipp Wendler