Linux namespace in Go - Part 1, UTS and PID

Linux namespace in Go - Part 1, UTS and PID

This article starts some Golang experiments on Linux namespace and provides context for Container technology. Linux namespace is an important foundation of container technology, it provides lightweight isolation between processes with Linux kernel support, therefore, different services can share the same machine with better resource utilization, great security.

The series of Linux namespace in Go:

Linux namespace

There’s a definition from Linux manual introducing Linux namespace:

A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes.

So, Linux namespace is the key that we can control the resources the processes can access.

Namespace types

What kind of isolation could we control is decided by the namespace types.

  • UTS
    Hostname and NIS domain name
  • Cgroup
    Controls the system resources (like CPU, Memory…) the process can use.
  • IPC
    POSIX message queues
  • Network
    Network devices, stacks, ports, etc.
  • Mount
    Mount points
  • PID
    Process IDs
  • Time
    Boot and monotonic clocks
  • User
    User and group IDs

“Go” through these types

Note that Linux namespace is only available in Linux distributions, I use Ubuntu 20.04 and Golang 1.14.2 here to run the experiments. If you’re using other OS, you might find the Linux namespace libraries missing, go and find a Linux machine and Ubuntu is recommended.

Note: The experiments code can be found in https://github.com/songrgg/namespace-demo

UTS Namespace

UTS will isolate the hostname for the forked process from its caller.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// folder v1
package main

import (
"fmt"
"os"
"os/exec"
"syscall"
)

func main() {
exec.Command("/bin/bash")
cmd := exec.Cmd{
Path: "/bin/bash",
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
SysProcAttr: &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS,
},
}
if err := cmd.Run(); err != nil {
fmt.Println(err)
}
}

This script needs sudo permission, run sudo go run main.go and it will create a new bash process with a new UTS namespace, you could modify hostname within this namespace and it won’t change the outside’s hostname.

1
2
3
4
5
6
7
8
9
10
11
12
[sojiang@ namespace-demo]$ hostname
sojiang.local
[sojiang@ namespace-demo]$ sudo go run exercise01/main.go
[root@ namespace-demo]# hostname
sojiang.local
[root@ namespace-demo]# hostname test.local
[root@ namespace-demo]# hostname
test.local
[root@ namespace-demo]# exit
exit
[sojiang@ namespace-demo]$ hostname
sojiang.local

PID namespace

PID namespace would create a new namespace for the process where the process ID is the same as the parent process, but note that you can only operate the processes under your namespace and can’t operate the processes in the parent namespace, in the opposite, the parent namespace has permission to operate the processes under the child namespaces.

Create a PID namespace simply by adding a CLONE_NEWPID flag:

1
2
3
SysProcAttr: &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID,
},

Run the process again,

1
2
3
4
5
6
[sojiang@ namespace-demo]$ sudo go run exercise02/main.go
[root@ namespace-demo]$ ps -ef
sojiang 6820 4062 0 06:44 ? /bin/zsh -i
root 7561 ... sudo go run exercise02/main.go
[root@ namespace-demo]$ kill -9 6820
bash: kill: (6820) - No such process

In the ps -ef output, we could see zsh which runs in the parent namespace and go run exercise02/main.go is running in the process’s namespace. We call the parent namespace P and the child namespace C, if we run sleep 100 in P, use ps -ef to get the process id and run kill -9 <process-id> in C, it will output “process not exist”. In the opposite, we could kill the process in C, that’s because the process visibility is in a single direction, only parent namespace could see all the processes in both P and C.

Like the following picture, pid 1 is in the parent namespace of pid Namespace x, so pid 1 could see all the processes, pid 3 could only see pid 3, pid 5 and pid 5.
Process hierachy

What’s next?

Here I did experiments on Linux UTS and PID namespaces, we know the isolation mechanism of them. I still have several questions,

  • How to run the program as other user instead of root?
  • I can still see the process list by ps -ef in the child namespace, however, most of the processes are in the parent namespace, there’s no need for me to see them, how to hide them or have my own process list?

The answer is in the Linux namespace in Go - Part 2, UID and Mount.

Reference

Comments

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×