Por que o child with mount namespace afeta as montagens pai?

3

Estou tentando entender os namespaces do Linux usando um servidor jessie do Debian no qual tenho acesso root.

Considere este código C:

# /tmp/test.c
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

static char child_stack[1048576];

static int my_child() {
  system("/bin/bash");
}

int main() {
  pid_t child_pid = clone(my_child, child_stack+1048576,
                          CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
  waitpid(child_pid, NULL, 0);
  return 0;
}

Em seguida, executo esses comandos em uma única sessão em um único terminal:

/tmp# id
uid=0(root) gid=0(root) groups=0(root),1093867019
/tmp# echo $$
1804
/tmp# ps -eaf | head
 UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 May08 ?        00:00:01 /sbin/init
root         2     0  0 May08 ?        00:00:00 [kthreadd]
root         3     2  0 May08 ?        00:00:00 [ksoftirqd/0]
root         5     2  0 May08 ?        00:00:00 [kworker/0:0H]
root         7     2  0 May08 ?        00:00:11 [rcu_sched]
root         8     2  0 May08 ?        00:00:00 [rcu_bh]
root         9     2  0 May08 ?        00:00:00 [migration/0]
root        10     2  0 May08 ?        00:00:00 [watchdog/0]
root        11     2  0 May08 ?        00:00:00 [khelper]
/tmp# grep /proc /proc/$$/mountinfo
15 19 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
33 15 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
/tmp# gcc test.c
/tmp# ./a.out
/tmp# echo $$
2
/tmp# echo $PPID
1
/tmp# ps -eaf | head
 UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 May08 ?        00:00:01 /sbin/init
root         2     0  0 May08 ?        00:00:00 [kthreadd]
root         3     2  0 May08 ?        00:00:00 [ksoftirqd/0]
root         5     2  0 May08 ?        00:00:00 [kworker/0:0H]
root         7     2  0 May08 ?        00:00:11 [rcu_sched]
root         8     2  0 May08 ?        00:00:00 [rcu_bh]
root         9     2  0 May08 ?        00:00:00 [migration/0]
root        10     2  0 May08 ?        00:00:00 [watchdog/0]
root        11     2  0 May08 ?        00:00:00 [khelper]
tmp# grep /proc /proc/$$/mountinfo
15 19 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
33 15 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=0,timeout=300,minproto=5,maxproto=5,direct
/tmp# mount -t proc proc /proc
/tmp# grep /proc /proc/$$/mountinfo
92 70 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
93 92 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=0,timeout=300,minproto=5,maxproto=5,direct
97 92 0:34 / /proc rw,relatime shared:27 - proc proc rw
/tmp# ps -eaf
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 07:31 pts/0    00:00:00 ./a.out
root         2     1  0 07:31 pts/0    00:00:00 /bin/bash
root        14     2  0 07:31 pts/0    00:00:00 ps -eaf
/tmp# exit
exit
/tmp# echo $$
1804
/tmp# grep /proc /proc/$$/mountinfo
grep: /proc/1804/mountinfo: No such file or directory
/tmp# ps -eaf
Error, do this: mount -t proc proc /proc
/tmp# mount -t proc proc /proc
/tmp# grep /proc /proc/$$/mountinfo
15 19 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
33 15 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
98 15 0:34 / /proc rw,relatime shared:27 - proc proc rw
69 98 0:3 / /proc rw,relatime shared:28 - proc proc rw

Por que, depois de sair do processo filho, /proc não está montado? Nenhuma alteração nos pontos de montagem feitos pelo processo filho deve afetar os pontos de montagem do pai? Isso parece contradizer a resposta dada por Emmet à pergunta link .

    
por rlandster 08.05.2016 / 16:44

1 resposta

3

Quando um processo filho é criado com clone com o sinalizador CLONE_NEWNS , o processo filho recebe seu próprio namespace de montagem. As operações de montagem ( mount , umount , mount --bind , etc.) no namespace filho têm apenas um efeito dentro desse namespace e as operações de montagem no namespace pai têm efeito apenas fora do novo namespace.

Exceto, isto é, para montagens compartilhadas. Uma montagem pode ser compartilhada , em cujo caso as operações afetam todos os namespaces em que a montagem é compartilhada. Um caso de uso típico para montagens compartilhadas é disponibilizar unidades removíveis em namespaces filhos, como chroots. Existem mais tipos de relacionamentos (montagens privadas, montagens não acessíveis); para mais detalhes, veja a documentação do kernel .

Você pode verificar se uma montagem é compartilhada verificando /proc/PID/mountinfo : se a linha contiver shared:NUMBER , a montagem é compartilhada e o número é um valor exclusivo que identifica o conjunto de namespaces com os quais ele é compartilhado. Se a linha não contiver tal indicação, a montagem é privada.

No seu sistema, /proc é compartilhado. Quando você monta uma nova instância de proc no namespace filho, já que você está montando sobre /proc do pai, essa nova instância também é compartilhada, portanto, é visível tanto no namespace filho quanto no namespace pai. Quando você sai do namespace filho, a segunda instância de /proc permanece montada, já que é compartilhada com o namespace pai ainda ativo.

Duas coisas complicam seu cenário: você também está criando um namespace PID e está usando /proc como assunto da experiência e como meio de observação. Quando ps reclama sobre /proc não estar montado, ele está realmente exibindo uma mensagem de erro enganosa - o errado proc está montado (a proc para o namespace errado). Você pode observar isso com ls /proc e cat /proc/1/mountinfo . Eu recomendo fazer os experimentos com um sistema de arquivos scratch, seria mais fácil entender o que estava acontecendo.

parent# ./a.out
child# echo $$
2
child# ls /proc
This is the parent's proc, with /proc/PID in the parent PID namespace
child# ps 1
… init
child# mount -t proc proc /proc
Now /proc in the child mount namespace is for the child PID namespace
child# ps 1
… a.out
child# exit
parent#

Até agora, não importava se /proc era particular ou compartilhado, mas agora é. Se /proc for privado, nesse ponto, observaremos o /proc do pai, que nunca foi afetado e mostra o namespace PID. Mas se /proc for compartilhado, o comando mount que emitimos anteriormente afetou os dois namespaces:

parent# ls /proc
acpi asound buddyinfo …
parent# ps 1
Error, do this: mount -t proc proc /proc
Actually, /proc is mounted, but it's the proc for the PID namespace that we created earlier and now has zero running processes.
parent# grep -c ' /proc ' /proc/mounts
2
parent# umount /proc
We've unmounted the child PID namespace's /proc that was shadowing the parent namespace's /proc, so the “normal” /proc is visible again.
parent# ps 1
… init
    
por 09.05.2016 / 00:55