Namespaces and Go Part 3 – Proc and Mount

In Part 2 we executed a shell with modified hostname using UTS namespace

In this article we will explain how we can use PID and Mount namespaces

By isolating mount and process namespaces , we will be able to give the container the impression that its having exclusive access to /proc
Which will make all process related commands to fetch information from isolated /proc

Lets see how we can implement this in go

We can modify ‘startContainer’ function discussed in Part 2 for mounting proc file system in MNT namespace

if err := syscall.Mount("proc", filepath.Join(root, "/proc"), "proc", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), ""); err != nil {
                fmt.Println("Proc mount failed")

As per mount man page the flags mentioned with syscall.Mount are below
MS_NOEXEC 8 /* Disallow program execution */
MS_NOSUID 2 /* Ignore suid and sgid bits */
MS_NODEV 4 /* Disallow access to device special files */

This will make sure the continer won’t get any special access via /proc

To get mount to work , we need to add namespace syscall.CLONE_NEWNS to Cloneflags in ‘setNameSpaces‘ function
Along with that we will add one more flag syscall.CLONE_NEWPID so that we will get isolated PIDs and process related commands will give isolated details because of mounted /proc.

So lets combine all above codes and build modified program

package main

import (

//Function to change Arg[0] to "fork" and set namespaces
//Finally call the binary itself  **
func setNameSpaces(shell string) {
        cmd := &exec.Cmd{
                Path: os.Args[0],
                Args: append([]string{"fork"}, shell),
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        cmd.SysProcAttr = &syscall.SysProcAttr{
                Cloneflags: syscall.CLONE_NEWUSER |
                        syscall.CLONE_NEWPID |
                        syscall.CLONE_NEWNS |
                UidMappings: []syscall.SysProcIDMap{
                                ContainerID: 0,
                                HostID:      os.Getuid(),
                                Size:        1,
                GidMappings: []syscall.SysProcIDMap{
                                ContainerID: 0,
                                HostID:      os.Getgid(),
                                Size:        1,
        cmd.Run() // path=./executable , Args[0]=fork , Args[1]=bash

//Set new hostname in already initialized namespace and start a shell
func startContainer(shell string) {
        fmt.Println("Starting Container")
        //Set a new hostname for our container
        if err := syscall.Sethostname([]byte("container")); err != nil {
                fmt.Printf("Setting Hostname failed")
        //Mount /proc
        //Mount /proc to new root's  proc directory using MNT namespace
        if err := syscall.Mount("proc", "/proc", "proc", uintptr(syscall.MS_NOEXEC|syscall.MS_NOSUID|syscall.MS_NODEV), ""); err != nil {
                fmt.Println("Proc mount failed")

        if err := syscall.Exec(shell, []string{""}, os.Environ()); err != nil {
                fmt.Println("Exec failed")


func main() {
        // Get absolute path to bash
        shell, err := exec.LookPath("bash")
        if err != nil {
                fmt.Printf("Bash not found\n")
        //This condition will fail first time as the Args[0] will be the name of program
        //But this condition will become true when ,
        //this program itslef calls with Arg[0] = "fork" from startProc() **
        if os.Args[0] == "fork" {
        //Starting point


[email protected]:~/.../uts_demo> go build uts_mnt_demo.go
[email protected]:~/.../uts_demo> ./uts_mnt_demo
Starting Container
container:~/.../uts_demo # ps -ef
root         1     0  1 23:47 pts/1    00:00:00
root        23     1  0 23:47 pts/1    00:00:00 ps -ef
container:~/.../uts_demo # cd /proc/
container:/proc # df .
Filesystem     1K-blocks  Used Available Use% Mounted on
proc                   0     0         0    - /proc
container:/proc # echo $$
container:/proc # exit

In next article we will change the root file system of the container so that we can completely isolate the process
This will be again be done with mount namespace , but we will use busybox and pivot_root operation to accomplish more isolation and ‘container-ish’ feel


You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *