github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/runtime/engines/oci/prepare.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 oci 7 8 import ( 9 "fmt" 10 "os" 11 12 "github.com/containerd/cgroups" 13 "github.com/sylabs/singularity/internal/pkg/sylog" 14 "github.com/sylabs/singularity/pkg/ociruntime" 15 16 "github.com/kr/pty" 17 specs "github.com/opencontainers/runtime-spec/specs-go" 18 "github.com/sylabs/singularity/internal/pkg/runtime/engines/config/starter" 19 "github.com/sylabs/singularity/pkg/util/capabilities" 20 ) 21 22 // make master/slave as global variable to avoid GC close file descriptor 23 var ( 24 master *os.File 25 slave *os.File 26 ) 27 28 func (e *EngineOperations) checkCapabilities() error { 29 for _, cap := range e.EngineConfig.OciConfig.Process.Capabilities.Permitted { 30 if _, ok := capabilities.Map[cap]; !ok { 31 return fmt.Errorf("Unrecognized capabilities %s", cap) 32 } 33 } 34 for _, cap := range e.EngineConfig.OciConfig.Process.Capabilities.Effective { 35 if _, ok := capabilities.Map[cap]; !ok { 36 return fmt.Errorf("Unrecognized capabilities %s", cap) 37 } 38 } 39 for _, cap := range e.EngineConfig.OciConfig.Process.Capabilities.Inheritable { 40 if _, ok := capabilities.Map[cap]; !ok { 41 return fmt.Errorf("Unrecognized capabilities %s", cap) 42 } 43 } 44 for _, cap := range e.EngineConfig.OciConfig.Process.Capabilities.Bounding { 45 if _, ok := capabilities.Map[cap]; !ok { 46 return fmt.Errorf("Unrecognized capabilities %s", cap) 47 } 48 } 49 for _, cap := range e.EngineConfig.OciConfig.Process.Capabilities.Ambient { 50 if _, ok := capabilities.Map[cap]; !ok { 51 return fmt.Errorf("Unrecognized capabilities %s", cap) 52 } 53 } 54 return nil 55 } 56 57 // PrepareConfig checks and prepares the runtime engine config 58 func (e *EngineOperations) PrepareConfig(starterConfig *starter.Config) error { 59 if e.CommonConfig.EngineName != Name { 60 return fmt.Errorf("incorrect engine") 61 } 62 63 if starterConfig.GetIsSUID() { 64 return fmt.Errorf("SUID workflow disabled by administrator") 65 } 66 67 if e.EngineConfig.OciConfig.Process == nil { 68 return fmt.Errorf("empty OCI process configuration") 69 } 70 71 if e.EngineConfig.OciConfig.Linux == nil { 72 return fmt.Errorf("empty OCI linux configuration") 73 } 74 75 // reset state config that could be passed to engine 76 e.EngineConfig.State = ociruntime.State{} 77 78 var gids []int 79 80 uid := int(e.EngineConfig.OciConfig.Process.User.UID) 81 gid := e.EngineConfig.OciConfig.Process.User.GID 82 83 gids = append(gids, int(gid)) 84 for _, g := range e.EngineConfig.OciConfig.Process.User.AdditionalGids { 85 gids = append(gids, int(g)) 86 } 87 88 starterConfig.SetTargetUID(uid) 89 starterConfig.SetTargetGID(gids) 90 91 if !e.EngineConfig.Exec { 92 starterConfig.SetInstance(true) 93 } 94 95 userNS := false 96 for _, ns := range e.EngineConfig.OciConfig.Linux.Namespaces { 97 if ns.Type == specs.UserNamespace { 98 userNS = true 99 break 100 } 101 } 102 103 starterConfig.SetNsFlagsFromSpec(e.EngineConfig.OciConfig.Linux.Namespaces) 104 if err := starterConfig.SetNsPathFromSpec(e.EngineConfig.OciConfig.Linux.Namespaces); err != nil { 105 return err 106 } 107 108 if userNS { 109 if len(e.EngineConfig.OciConfig.Linux.UIDMappings) == 0 { 110 return fmt.Errorf("user namespace invoked without uid mapping") 111 } 112 if len(e.EngineConfig.OciConfig.Linux.GIDMappings) == 0 { 113 return fmt.Errorf("user namespace invoked without gid mapping") 114 } 115 if err := starterConfig.AddUIDMappings(e.EngineConfig.OciConfig.Linux.UIDMappings); err != nil { 116 return err 117 } 118 if err := starterConfig.AddGIDMappings(e.EngineConfig.OciConfig.Linux.GIDMappings); err != nil { 119 return err 120 } 121 } 122 123 if e.EngineConfig.OciConfig.Linux.RootfsPropagation != "" { 124 starterConfig.SetMountPropagation(e.EngineConfig.OciConfig.Linux.RootfsPropagation) 125 } else { 126 starterConfig.SetMountPropagation("private") 127 } 128 129 starterConfig.SetNoNewPrivs(e.EngineConfig.OciConfig.Process.NoNewPrivileges) 130 131 if e.EngineConfig.OciConfig.Process.Capabilities != nil { 132 if err := e.checkCapabilities(); err != nil { 133 return err 134 } 135 136 // force cap_sys_admin for seccomp and no_new_priv flag 137 caps := append(e.EngineConfig.OciConfig.Process.Capabilities.Effective, "CAP_SYS_ADMIN") 138 starterConfig.SetCapabilities(capabilities.Effective, caps) 139 140 caps = append(e.EngineConfig.OciConfig.Process.Capabilities.Permitted, "CAP_SYS_ADMIN") 141 starterConfig.SetCapabilities(capabilities.Permitted, caps) 142 143 starterConfig.SetCapabilities(capabilities.Inheritable, e.EngineConfig.OciConfig.Process.Capabilities.Inheritable) 144 starterConfig.SetCapabilities(capabilities.Bounding, e.EngineConfig.OciConfig.Process.Capabilities.Bounding) 145 starterConfig.SetCapabilities(capabilities.Ambient, e.EngineConfig.OciConfig.Process.Capabilities.Ambient) 146 } 147 148 e.EngineConfig.MasterPts = -1 149 e.EngineConfig.SlavePts = -1 150 e.EngineConfig.OutputStreams = [2]int{-1, -1} 151 e.EngineConfig.ErrorStreams = [2]int{-1, -1} 152 e.EngineConfig.InputStreams = [2]int{-1, -1} 153 154 if e.EngineConfig.GetLogFormat() == "" { 155 sylog.Debugf("No log format specified, setting kubernetes log format by default") 156 e.EngineConfig.SetLogFormat("kubernetes") 157 } 158 159 if !e.EngineConfig.Exec { 160 if e.EngineConfig.OciConfig.Process.Terminal { 161 var err error 162 163 master, slave, err = pty.Open() 164 if err != nil { 165 return err 166 } 167 consoleSize := e.EngineConfig.OciConfig.Process.ConsoleSize 168 if consoleSize != nil { 169 var size pty.Winsize 170 171 size.Cols = uint16(consoleSize.Width) 172 size.Rows = uint16(consoleSize.Height) 173 if err := pty.Setsize(slave, &size); err != nil { 174 return err 175 } 176 } 177 e.EngineConfig.MasterPts = int(master.Fd()) 178 e.EngineConfig.SlavePts = int(slave.Fd()) 179 } else { 180 r, w, err := os.Pipe() 181 if err != nil { 182 return err 183 } 184 e.EngineConfig.OutputStreams = [2]int{int(r.Fd()), int(w.Fd())} 185 r, w, err = os.Pipe() 186 if err != nil { 187 return err 188 } 189 e.EngineConfig.ErrorStreams = [2]int{int(r.Fd()), int(w.Fd())} 190 r, w, err = os.Pipe() 191 if err != nil { 192 return err 193 } 194 e.EngineConfig.InputStreams = [2]int{int(w.Fd()), int(r.Fd())} 195 } 196 } else { 197 starterConfig.SetJoinMount(true) 198 cPath := e.EngineConfig.OciConfig.Linux.CgroupsPath 199 if cPath == "" { 200 return nil 201 } 202 203 // add executed process to container cgroups 204 ppid := os.Getppid() 205 staticPath := cgroups.StaticPath(cPath) 206 control, err := cgroups.Load(cgroups.V1, staticPath) 207 if err != nil { 208 return fmt.Errorf("failed to load cgroups: %s", err) 209 } 210 if err := control.Add(cgroups.Process{Pid: ppid}); err != nil { 211 return fmt.Errorf("failed to add exec process to cgroups %s: %s", cPath, err) 212 } 213 } 214 215 return nil 216 }