0

I am trying to understand how linux containers work internally. One of the things that I have learned is that you can containerize a shell by passing in special flags to it. Out of the flags one is for the PIDs.

This however, does not give the desired effect for the ps command, as it still shows all the processes from the host and the container. This is probably because ps reads from /proc directory.

One of the methods I have seen to get ps to show processes that are running only on the container is via chrooting into a fake root file system (which just contains the directories/utilities of the operating system) and then mounting the /proc directory of the host to the proc directory of the fake fs.

I just can't understand why this actually works. Why does mounting the /proc directory into another mount point make it behave like it is containerized.

Why do you even need a fake root filesystems for containers like Docker to work properly?

Am I missing something?

The technique that I describe is from this video at DockerCon.

I have set the link to the exact time this method is done.

  • What special flags (in the 1st paragraph)? – ctrl-alt-delor Dec 2 '18 at 17:16
  • @ctrl-alt-delor Special flags as in CLONE_NEWPID and CLONE_NEWUTS – ng.newbie Dec 2 '18 at 17:53
  • I just watched the video (I like it). Those flags are not passed to the shell. They are set before the shell is run (via exec). – ctrl-alt-delor Dec 2 '18 at 18:16
3

Assuming you're talking of the unshare command, the solution would be to use the --mount-proc option, which also unshares the mount namespace and mounts a new /proc in there which will refer to the new pid namespace. See the pid_namespaces(7) man page:

/proc and PID namespaces

A /proc filesystem shows (in the /proc/[pid] directories) only processes visible in the PID namespace of the process that performed the mount, even if the /proc filesystem is viewed from processes in other namespaces.

After creating a new PID namespace, it is useful for the child to change its root directory and mount a new procfs instance at /proc so that tools such as ps(1) work correctly.

If a new mount namespace is simultaneously created by including CLONE_NEWNS in the flags argument of clone(2) or unshare(2), then it isn't necessary to change the root directory: a new procfs instance can be mounted directly over /proc.

From a shell, the command to mount /proc is:

$ mount -t proc proc /proc

Calling readlink(2) on the path /proc/self yields the process ID of the caller in the PID namespace of the procfs mount (i.e., the PID namespace of the process that mounted the procfs). This can be useful for introspection purposes, when a process wants to discover its PID in other namespaces.

$ sudo unshare -p   -f ps -o pid,ppid,pidns,mntns,comm
  PID  PPID      PIDNS      MNTNS COMMAND
27462 24107 4026531836 4026531840 sudo
27463 27462 4026531836 4026531840 unshare
27464 27463 4026532863 4026531840 ps
$ sudo unshare -p --mount-proc  -f ps -o pid,ppid,pidns,mntns,comm
  PID  PPID      PIDNS      MNTNS COMMAND
    1     0 4026532864 4026532863 ps

unshare and nsenter are the low level utilities that wrap the unshare(2) and setns(2) system calls used to access namespaces like docker does.

You can strace them to see what happens. In the second one:

  1. unshare the mnt and pid namespace:

    5281  unshare(CLONE_NEWNS|CLONE_NEWPID) = 0
    
  2. fork a child (because of -f)

    5281  clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f6b0af4a7d0) = 5282
    

    the child inherits the unshared namespaces

  3. disable mount propagation in the new mnt namespace so that mounts propagate neither in parent nor child namespaces:

    5282  mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) = 0
    5282  mount("none", "/proc", NULL, MS_REC|MS_PRIVATE, NULL) = 0
    
  4. mount a new proc for the new pid namespace inside /proc (as that's where ps expects to find it and why we created a mnt namespace). Another option would be to use some bind-mounts and chroot. You could also mount the pid namespace proc fs into the parent mnt namespace, but that would cause havoc.

    5282  mount("proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL) = 0
    
  5. execute ps in that namespace

    5282  execve("/bin/ps", ["ps", "-o", "pid,ppid,pidns,mntns,comm"], 0x7fff5a325dd8 /* 73 vars */) = 0
    
  • No I am not talking about the unshare command. The video that I have linked does not do that. It just mounts the /proc in another directory in a chroot jail. It makes no sense why it works. Just a huge favor to me can you please once go through the video linked? – ng.newbie Dec 2 '18 at 17:14
  • 1
    I just looked at the video. She mounts the type proc, as /proc, and it only reveals as much as it can (it is in a different process namespace). When you can see the /proc from the host namespace, there is (as you have said) a leak in the process namespace. – ctrl-alt-delor Dec 2 '18 at 17:23
  • 1
    No it is not a remount (note the lack of remount options). Can you have several procs. Try it, yes but in different places, ps will only use the one in /proc. You may be able to replace it with static files, to trick ps. However only one /proc can exist, whether a file, directory, or special (unless you do a chroot). – ctrl-alt-delor Dec 2 '18 at 17:31
  • 1
    You are mixing a few things: There are various name spaces. The video showed hostname namespace (she did not focus on this much), process-namespace, and file-system namespace (via chroot). You are mixing file-system, and process namespace. Probably because ps used the file-system (/proc), but you would expect it to use the process-namespace. proc uses process namespace, so when a new proc is created, it is in the new namespace. – ctrl-alt-delor Dec 2 '18 at 18:12
  • 1
    No the kernel does not write to two places at the same time (there is nothing to write to (this is not disk space). When you read from proc, the kernel generates the appropriate data, this will depend on what namespace you are in. – ctrl-alt-delor Dec 2 '18 at 18:13
0

I just looked at the video. She mounts the type proc, as /proc, and it only reveals as much as it can (it is in a different process namespace). When you can see the hosts /proc, there is (as you have said) a leak in the process namespace.

You are mixing a few things: There are various name spaces. The video showed hostname namespace (she did not focus on this much), process-namespace, and file-system namespace (via chroot). You are mixing file-system, and process namespace. Probably because ps used the file-system (/proc), but you would expect it to use the process-namespace. proc uses process namespace, so when a new proc is created, it is in the new namespace.

Mounting

Note the mount is not a remount (note the lack of remount options).

Can you have several procs? Try it. Yes, but in different places, ps will only use the one in /proc. You may even be able to replace it with static files, to trick ps. However only one /proc can exist, whether a file, directory, or special (unless you do a chroot)

Does the kernel write to all of the procs

No the kernel does not write to two places at the same time (there is nothing to write to (this is not disk space). When you read from proc, the kernel generates the appropriate data, (this will depend on what namespace you are in). You can only read one at a time, so only one it created at a time, and only as much as is needed (just the one file).

Is there any documentation which namespaces correspond to which filesystems in Linux?

That is like asking what namespace ls is in. It is not, but it is affected by the namespaces. Thus proc, can only know about processes within the current process-namespace. Other parts of proc may be affected by other name-space types (depending on their function).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.