github.com/hpcng/singularity@v3.1.1+incompatible/internal/pkg/runtime/engines/config/starter/starter.go (about) 1 // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package starter 7 8 /* 9 #include <stdlib.h> 10 #include <string.h> 11 #include <sys/mman.h> 12 #include <sys/types.h> 13 #include "starter.h" 14 */ 15 // #cgo CFLAGS: -I../../../../../../cmd/starter/c/include 16 import "C" 17 import ( 18 "encoding/json" 19 "fmt" 20 "strings" 21 "syscall" 22 "unsafe" 23 24 "github.com/opencontainers/runtime-spec/specs-go" 25 "github.com/sylabs/singularity/internal/pkg/sylog" 26 "github.com/sylabs/singularity/pkg/util/capabilities" 27 ) 28 29 // CConfig is the common type for C.struct_cConfig 30 type CConfig *C.struct_cConfig 31 32 // Config represents structure to manipulate C starter configuration 33 type Config struct { 34 config CConfig 35 nsPath []byte 36 } 37 38 // NewConfig takes a pointer to C starter configuration and returns a 39 // pointer to a Config 40 func NewConfig(config CConfig) *Config { 41 return &Config{config: config, nsPath: make([]byte, 1)} 42 } 43 44 // GetIsSUID returns if SUID workflow is enabled or not 45 func (c *Config) GetIsSUID() bool { 46 if c.config.container.isSuid == 1 { 47 return true 48 } 49 return false 50 } 51 52 // GetContainerPid returns container process ID 53 func (c *Config) GetContainerPid() int { 54 return int(c.config.container.pid) 55 } 56 57 // SetInstance sets if starter should spawn instance or not 58 func (c *Config) SetInstance(instance bool) { 59 if instance { 60 c.config.container.isInstance = C.uchar(1) 61 } else { 62 c.config.container.isInstance = C.uchar(0) 63 } 64 } 65 66 // GetInstance returns if container run as instance or not 67 func (c *Config) GetInstance() bool { 68 if c.config.container.isInstance == 1 { 69 return true 70 } 71 return false 72 } 73 74 // SetNoNewPrivs sets NO_NEW_PRIVS flag 75 func (c *Config) SetNoNewPrivs(noprivs bool) { 76 if noprivs { 77 c.config.container.noNewPrivs = C.uchar(1) 78 } else { 79 c.config.container.noNewPrivs = C.uchar(0) 80 } 81 } 82 83 // GetNoNewPrivs returns if NO_NEW_PRIVS flag is set or not 84 func (c *Config) GetNoNewPrivs() bool { 85 if c.config.container.noNewPrivs == 1 { 86 return true 87 } 88 return false 89 } 90 91 // SetSharedMount sets if master/container shares mount point 92 func (c *Config) SetSharedMount(shared bool) { 93 if shared { 94 c.config.container.sharedMount = C.uchar(1) 95 } else { 96 c.config.container.sharedMount = C.uchar(0) 97 } 98 } 99 100 // GetSharedMount returns if master/container shares mount point or not 101 func (c *Config) GetSharedMount() bool { 102 if c.config.container.sharedMount == 1 { 103 return true 104 } 105 return false 106 } 107 108 // SetJoinMount sets if container process join a mount namespace 109 func (c *Config) SetJoinMount(join bool) { 110 if join { 111 c.config.container.joinMount = C.uchar(1) 112 } else { 113 c.config.container.joinMount = C.uchar(0) 114 } 115 } 116 117 // GetJoinMount returns if container process join a mount namespace 118 func (c *Config) GetJoinMount() bool { 119 if c.config.container.joinMount == 1 { 120 return true 121 } 122 return false 123 } 124 125 // SetBringLoopbackInterface sets if starter bring loopback network interface 126 func (c *Config) SetBringLoopbackInterface(bring bool) { 127 if bring { 128 c.config.container.bringLoopbackInterface = C.uchar(1) 129 } else { 130 c.config.container.bringLoopbackInterface = C.uchar(0) 131 } 132 } 133 134 // GetBringLoopbackInterface returns if starter bring loopback network interface 135 func (c *Config) GetBringLoopbackInterface() bool { 136 if c.config.container.bringLoopbackInterface == 1 { 137 return true 138 } 139 return false 140 } 141 142 // SetMountPropagation sets root filesystem mount propagation 143 func (c *Config) SetMountPropagation(propagation string) { 144 var flags uintptr 145 146 switch propagation { 147 case "shared", "rshared": 148 flags = syscall.MS_SHARED 149 case "slave", "rslave": 150 flags = syscall.MS_SLAVE 151 case "private", "rprivate": 152 flags = syscall.MS_SHARED 153 case "unbindable", "runbindable": 154 flags = syscall.MS_UNBINDABLE 155 } 156 157 if strings.HasPrefix(propagation, "r") { 158 flags |= syscall.MS_REC 159 } 160 c.config.container.mountPropagation = C.ulong(flags) 161 } 162 163 // GetJSONConfig returns pointer to JSON configuration 164 func (c *Config) GetJSONConfig() []byte { 165 return C.GoBytes(unsafe.Pointer(&c.config.json.config[0]), C.int(c.config.json.size)) 166 } 167 168 // WriteConfig writes raw C configuration 169 func (c *Config) Write(payload interface{}) error { 170 jsonConf, err := json.Marshal(payload) 171 if err != nil { 172 return fmt.Errorf("failed to marshal payload: %s", err) 173 } 174 size := len(jsonConf) 175 maxSize := C.MAX_JSON_SIZE - 1 176 c.config.json.size = C.size_t(size) 177 178 if size >= maxSize { 179 return fmt.Errorf("json configuration too big %d > %d", size, maxSize) 180 } 181 182 json := C.CBytes(jsonConf) 183 184 C.memcpy(unsafe.Pointer(&c.config.json.config[0]), json, c.config.json.size) 185 C.free(json) 186 187 return nil 188 } 189 190 // AddUIDMappings sets user namespace UID mapping. 191 func (c *Config) AddUIDMappings(uids []specs.LinuxIDMapping) error { 192 uidMap := "" 193 for i, uid := range uids { 194 if i == 0 { 195 c.SetTargetUID(int(uid.ContainerID)) 196 } 197 uidMap = uidMap + fmt.Sprintf("%d %d %d\n", uid.ContainerID, uid.HostID, uid.Size) 198 } 199 200 l := len(uidMap) 201 if l >= C.MAX_MAP_SIZE-1 { 202 return fmt.Errorf("UID map too big") 203 } 204 205 if l > 0 { 206 cpath := unsafe.Pointer(C.CString(uidMap)) 207 size := C.size_t(l) 208 209 C.memcpy(unsafe.Pointer(&c.config.container.uidMap[0]), cpath, size) 210 C.free(cpath) 211 } 212 213 return nil 214 } 215 216 // AddGIDMappings sets user namespace GID mapping 217 func (c *Config) AddGIDMappings(gids []specs.LinuxIDMapping) error { 218 var targetGids []int 219 gidMap := "" 220 for _, gid := range gids { 221 targetGids = append(targetGids, int(gid.ContainerID)) 222 gidMap = gidMap + fmt.Sprintf("%d %d %d\n", gid.ContainerID, gid.HostID, gid.Size) 223 } 224 225 if len(targetGids) != 0 { 226 c.SetTargetGID(targetGids) 227 } 228 229 l := len(gidMap) 230 if l >= C.MAX_MAP_SIZE-1 { 231 return fmt.Errorf("GID map too big") 232 } 233 234 if l > 0 { 235 cpath := unsafe.Pointer(C.CString(gidMap)) 236 size := C.size_t(l) 237 238 C.memcpy(unsafe.Pointer(&c.config.container.gidMap[0]), cpath, size) 239 C.free(cpath) 240 } 241 242 return nil 243 } 244 245 // SetNsFlags sets namespaces flag directly from flags argument 246 func (c *Config) SetNsFlags(flags int) { 247 c.config.namespace.flags = C.uint(flags) 248 } 249 250 // SetNsFlagsFromSpec sets namespaces flag from OCI spec 251 func (c *Config) SetNsFlagsFromSpec(namespaces []specs.LinuxNamespace) { 252 c.config.namespace.flags = 0 253 for _, namespace := range namespaces { 254 if namespace.Path == "" { 255 switch namespace.Type { 256 case specs.UserNamespace: 257 c.config.namespace.flags |= syscall.CLONE_NEWUSER 258 case specs.IPCNamespace: 259 c.config.namespace.flags |= syscall.CLONE_NEWIPC 260 case specs.UTSNamespace: 261 c.config.namespace.flags |= syscall.CLONE_NEWUTS 262 case specs.PIDNamespace: 263 c.config.namespace.flags |= syscall.CLONE_NEWPID 264 case specs.NetworkNamespace: 265 c.config.namespace.flags |= syscall.CLONE_NEWNET 266 case specs.MountNamespace: 267 c.config.namespace.flags |= syscall.CLONE_NEWNS 268 case specs.CgroupNamespace: 269 c.config.namespace.flags |= 0x2000000 270 } 271 } 272 } 273 } 274 275 // SetNsPath sets corresponding namespace to be joined 276 func (c *Config) SetNsPath(nstype specs.LinuxNamespaceType, path string) error { 277 cpath := unsafe.Pointer(C.CString(path)) 278 l := len(path) 279 size := C.size_t(l) 280 281 if l > C.MAX_NS_PATH_SIZE-1 { 282 return fmt.Errorf("%s namespace path too big", nstype) 283 } 284 285 switch nstype { 286 case specs.UserNamespace: 287 C.memcpy(unsafe.Pointer(&c.config.namespace.user[0]), cpath, size) 288 case specs.IPCNamespace: 289 C.memcpy(unsafe.Pointer(&c.config.namespace.ipc[0]), cpath, size) 290 case specs.UTSNamespace: 291 C.memcpy(unsafe.Pointer(&c.config.namespace.uts[0]), cpath, size) 292 case specs.PIDNamespace: 293 C.memcpy(unsafe.Pointer(&c.config.namespace.pid[0]), cpath, size) 294 case specs.NetworkNamespace: 295 C.memcpy(unsafe.Pointer(&c.config.namespace.network[0]), cpath, size) 296 case specs.MountNamespace: 297 C.memcpy(unsafe.Pointer(&c.config.namespace.mount[0]), cpath, size) 298 case specs.CgroupNamespace: 299 C.memcpy(unsafe.Pointer(&c.config.namespace.cgroup[0]), cpath, size) 300 } 301 302 C.free(cpath) 303 304 return nil 305 } 306 307 // SetNsPathFromSpec sets corresponding namespace to be joined from OCI spec 308 func (c *Config) SetNsPathFromSpec(namespaces []specs.LinuxNamespace) error { 309 for _, namespace := range namespaces { 310 if namespace.Path != "" { 311 cpath := unsafe.Pointer(C.CString(namespace.Path)) 312 l := len(namespace.Path) 313 size := C.size_t(l) 314 315 if l > C.MAX_NS_PATH_SIZE-1 { 316 return fmt.Errorf("%s namespace path too big", namespace.Type) 317 } 318 319 switch namespace.Type { 320 case specs.UserNamespace: 321 C.memcpy(unsafe.Pointer(&c.config.namespace.user[0]), cpath, size) 322 case specs.IPCNamespace: 323 C.memcpy(unsafe.Pointer(&c.config.namespace.ipc[0]), cpath, size) 324 case specs.UTSNamespace: 325 C.memcpy(unsafe.Pointer(&c.config.namespace.uts[0]), cpath, size) 326 case specs.PIDNamespace: 327 C.memcpy(unsafe.Pointer(&c.config.namespace.pid[0]), cpath, size) 328 case specs.NetworkNamespace: 329 C.memcpy(unsafe.Pointer(&c.config.namespace.network[0]), cpath, size) 330 case specs.MountNamespace: 331 C.memcpy(unsafe.Pointer(&c.config.namespace.mount[0]), cpath, size) 332 case specs.CgroupNamespace: 333 C.memcpy(unsafe.Pointer(&c.config.namespace.cgroup[0]), cpath, size) 334 } 335 336 C.free(cpath) 337 } 338 } 339 340 return nil 341 } 342 343 // SetCapabilities sets corresponding capability set identified by ctype 344 // from a capability string list identified by ctype 345 func (c *Config) SetCapabilities(ctype string, caps []string) { 346 switch ctype { 347 case capabilities.Permitted: 348 c.config.capabilities.permitted = 0 349 for _, v := range caps { 350 c.config.capabilities.permitted |= C.ulonglong(1 << capabilities.Map[v].Value) 351 } 352 case capabilities.Effective: 353 c.config.capabilities.effective = 0 354 for _, v := range caps { 355 c.config.capabilities.effective |= C.ulonglong(1 << capabilities.Map[v].Value) 356 } 357 case capabilities.Inheritable: 358 c.config.capabilities.inheritable = 0 359 for _, v := range caps { 360 c.config.capabilities.inheritable |= C.ulonglong(1 << capabilities.Map[v].Value) 361 } 362 case capabilities.Bounding: 363 c.config.capabilities.bounding = 0 364 for _, v := range caps { 365 c.config.capabilities.bounding |= C.ulonglong(1 << capabilities.Map[v].Value) 366 } 367 case capabilities.Ambient: 368 c.config.capabilities.ambient = 0 369 for _, v := range caps { 370 c.config.capabilities.ambient |= C.ulonglong(1 << capabilities.Map[v].Value) 371 } 372 } 373 } 374 375 // SetTargetUID sets target UID to execute the container process as user ID 376 func (c *Config) SetTargetUID(uid int) { 377 c.config.container.targetUID = C.uid_t(uid) 378 } 379 380 // SetTargetGID sets target GIDs to execute container process as group IDs 381 func (c *Config) SetTargetGID(gids []int) { 382 c.config.container.numGID = C.int(len(gids)) 383 384 for i, gid := range gids { 385 if i > C.MAX_GID { 386 sylog.Warningf("you can't specify more than %d group IDs", C.MAX_GID) 387 break 388 } 389 c.config.container.targetGID[i] = C.gid_t(gid) 390 } 391 } 392 393 // Release performs a unmap on starter config and release mapped memory 394 func (c *Config) Release() error { 395 if C.munmap(unsafe.Pointer(c.config), C.sizeof_struct_cConfig) != 0 { 396 return fmt.Errorf("failed to release starter memory") 397 } 398 return nil 399 }