-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.go
148 lines (126 loc) · 3.66 KB
/
run.go
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2018 Roberto Mier Escandon <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package container
import (
"errors"
"fmt"
"log"
"os"
"os/exec"
"syscall"
)
const (
prompt = "[container] # "
selfProc = "/proc/self/exe"
)
func init() {
// Prevent infinite recursive calls to child
if os.Args[0] == selfProc {
if len(os.Args) <= 1 {
fmt.Println("Please provide more parameters")
os.Exit(1)
}
reexec(os.Args[1:])
os.Exit(0)
}
}
// Run runs command in a new container
func Run(args []string) error {
if len(args) == 0 {
return errors.New("You must provide a command to run into the container")
}
// Reexec self process (fork) with cloned namespaces and additional
// configuration to isolate the command execution
cmd := exec.Command(selfProc, args[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
cmd.SysProcAttr = &syscall.SysProcAttr{
// Namespaces to clone
Cloneflags: syscall.CLONE_NEWNS |
syscall.CLONE_NEWUTS |
// syscall.CLONE_NEWIPC |
syscall.CLONE_NEWPID |
syscall.CLONE_NEWNET |
syscall.CLONE_NEWUSER,
// Map container to host users and groups
UidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: os.Getuid(),
Size: 1,
},
},
GidMappings: []syscall.SysProcIDMap{
{
ContainerID: 0,
HostID: os.Getegid(),
Size: 1,
},
},
// Maintain the root permissions for configuring network into the container
// FIXME: execute only the network configuration as root
Credential: &syscall.Credential{Uid: 0},
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("Could not start command execution - %s", err)
}
c, err := loadCfg()
if err != nil {
return fmt.Errorf("Could not read the settings - %s", err)
}
if err := configureNetworkOnHost(c.Network.Bridge, c.Network.Veth, c.Network.CIDR, cmd.Process.Pid); err != nil {
return err
}
if err := cmd.Wait(); err != nil {
return fmt.Errorf("Error waiting for the command execution to finish - %s", err)
}
return nil
}
func reexec(args []string) {
// Setup container hostname
// (It'll be shown as container prompt)
hostname := fmt.Sprintf("container-%v", randStr(6))
assert(syscall.Sethostname([]byte(hostname)))
c, err := loadCfg()
assert(err)
// Mount /proc.
// This MUST be done BEFORE PIVOTING. Otherwise it wont be allowed to do it.
// (now you can check that ps reports only container processes ids
// and that ls -lah /proc/mounts reports only container mounts but not host's)
assert(mountProc(c.Rootfs))
// Pivot root to configured rootfs
// (now you can check that we have moved to the new path)
assert(pivotRoot(c.Rootfs))
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = []string{fmt.Sprintf("PS1=[%v] # ", hostname)}
assert(cmd.Start())
// Configure veth1 interface
assert(configureNetworkOnContainer(c.Network.Veth, c.Network.CIDR, cmd.Process.Pid))
assert(cmd.Wait())
}
func assert(err error) {
if err != nil {
log.Println(err)
os.Exit(1)
}
}