forked from nestybox/sysbox-runc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
spec.go
181 lines (152 loc) · 4.59 KB
/
spec.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// +build linux
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"math"
"os"
"strconv"
"strings"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libsysbox/syscont"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/urfave/cli"
)
var specCommand = cli.Command{
Name: "spec",
Usage: "create a new system container specification file",
Description: `The spec command creates the new system container specification file
named "` + specConfig + `" for the bundle.
The spec generated is just a starter file. Editing of the spec is required to
achieve desired results.
ID mapping configuration:
Nestybox system containers always use the Linux user namespace and thus require user
and group ID mappings.
The "--id-map" option allows configuration of these mappings for the generated spec.
It's normally not required, unless the user wants to control the user and group IDs
mappings of the container.
If the "--id-map" option is omitted, the generated spec will not include the
user and group ID mappings. In this case sysbox-runc will automatically
allocate them when the container is created. The allocation is done in such as
way as to provide each sys container an exclusive range of uid(gid)s on the
host, as a means to improve isolation. This feature requires that the
container's root filesystem be owned by "root:root".
If the "--id-map" option is given, the generated spec will include them and
sysbox-runc will honor them when creating the container. They are expected
to match the container's root filesystem ownership. Note that the size of the
range is required be >= ` + strconv.FormatUint(uint64(syscont.IdRangeMin), 10) + ` (for compatibility with Linux distros
that use ID 65534 as "nobody").
`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "bundle, b",
Value: "",
Usage: "path to the sys container's bundle directory",
},
cli.StringFlag{
Name: "id-map, m",
Value: "",
Usage: `"uid gid size" ID mappings (see description above)`,
},
},
Action: func(context *cli.Context) error {
var uid, gid, size uint32
idMap := context.String("id-map")
if idMap != "" {
if err := parseIDMap(idMap, &uid, &gid, &size); err != nil {
return err
}
}
spec, err := syscont.Example()
if err != nil {
return err
}
if idMap != "" {
spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{Type: "user"})
spec.Linux.UIDMappings = []specs.LinuxIDMapping{{
HostID: uid,
ContainerID: 0,
Size: size,
}}
spec.Linux.GIDMappings = []specs.LinuxIDMapping{{
HostID: gid,
ContainerID: 0,
Size: size,
}}
}
bundle := context.String("bundle")
if bundle != "" {
if err := os.Chdir(bundle); err != nil {
return err
}
}
checkNoFile := func(name string) error {
_, err := os.Stat(name)
if err == nil {
return fmt.Errorf("File %s exists. Remove it first", name)
}
if !os.IsNotExist(err) {
return err
}
return nil
}
if err := checkNoFile(specConfig); err != nil {
return err
}
data, err := json.MarshalIndent(spec, "", "\t")
if err != nil {
return err
}
return ioutil.WriteFile(specConfig, data, 0666)
},
}
// parseIDMap parses the id-map flag and returns the uid, gid, and size
func parseIDMap(idMap string, uid, gid, size *uint32) error {
var num [3]uint64
var err error
fields := strings.Fields(idMap)
if len(fields) != 3 {
return fmt.Errorf("id-map must be of the form \"uid gid size\"; got %v", idMap)
}
for i, f := range fields {
num[i], err = strconv.ParseUint(f, 10, 32)
if err != nil {
return err
}
}
*uid = uint32(num[0])
*gid = uint32(num[1])
*size = uint32(num[2])
if *uid > math.MaxUint32 || *gid > math.MaxUint32 || *size < syscont.IdRangeMin {
return fmt.Errorf("invalid id-map \"%v\": uid and gid must be <= %v, size must be >= %v",
idMap, uint32(math.MaxUint32), syscont.IdRangeMin)
}
return nil
}
// loadSpec loads the specification from the provided path
func loadSpec(cPath string) (spec *specs.Spec, err error) {
cf, err := os.Open(cPath)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("JSON specification file %s not found", cPath)
}
return nil, err
}
defer cf.Close()
if err = json.NewDecoder(cf).Decode(&spec); err != nil {
return nil, err
}
return spec, validateProcessSpec(spec.Process)
}
func createLibContainerRlimit(rlimit specs.POSIXRlimit) (configs.Rlimit, error) {
rl, err := strToRlimit(rlimit.Type)
if err != nil {
return configs.Rlimit{}, err
}
return configs.Rlimit{
Type: rl,
Hard: rlimit.Hard,
Soft: rlimit.Soft,
}, nil
}