github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/specgen/generate/oci.go (about) 1 package generate 2 3 import ( 4 "strings" 5 6 "github.com/containers/libpod/libpod" 7 "github.com/containers/libpod/libpod/image" 8 "github.com/containers/libpod/pkg/rootless" 9 createconfig "github.com/containers/libpod/pkg/spec" 10 "github.com/containers/libpod/pkg/specgen" 11 spec "github.com/opencontainers/runtime-spec/specs-go" 12 "github.com/opencontainers/runtime-tools/generate" 13 ) 14 15 func SpecGenToOCI(s *specgen.SpecGenerator, rt *libpod.Runtime, newImage *image.Image) (*spec.Spec, error) { 16 var ( 17 inUserNS bool 18 ) 19 cgroupPerm := "ro" 20 g, err := generate.New("linux") 21 if err != nil { 22 return nil, err 23 } 24 // Remove the default /dev/shm mount to ensure we overwrite it 25 g.RemoveMount("/dev/shm") 26 g.HostSpecific = true 27 addCgroup := true 28 canMountSys := true 29 30 isRootless := rootless.IsRootless() 31 if isRootless { 32 inUserNS = true 33 } 34 if !s.UserNS.IsHost() { 35 if s.UserNS.IsContainer() || s.UserNS.IsPath() { 36 inUserNS = true 37 } 38 if s.UserNS.IsPrivate() { 39 inUserNS = true 40 } 41 } 42 if inUserNS && s.NetNS.IsHost() { 43 canMountSys = false 44 } 45 46 if s.Privileged && canMountSys { 47 cgroupPerm = "rw" 48 g.RemoveMount("/sys") 49 sysMnt := spec.Mount{ 50 Destination: "/sys", 51 Type: "sysfs", 52 Source: "sysfs", 53 Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, 54 } 55 g.AddMount(sysMnt) 56 } else if !canMountSys { 57 addCgroup = false 58 g.RemoveMount("/sys") 59 r := "ro" 60 if s.Privileged { 61 r = "rw" 62 } 63 sysMnt := spec.Mount{ 64 Destination: "/sys", 65 Type: "bind", // should we use a constant for this, like createconfig? 66 Source: "/sys", 67 Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"}, 68 } 69 g.AddMount(sysMnt) 70 if !s.Privileged && isRootless { 71 g.AddLinuxMaskedPaths("/sys/kernel") 72 } 73 } 74 gid5Available := true 75 if isRootless { 76 nGids, err := createconfig.GetAvailableGids() 77 if err != nil { 78 return nil, err 79 } 80 gid5Available = nGids >= 5 81 } 82 // When using a different user namespace, check that the GID 5 is mapped inside 83 // the container. 84 if gid5Available && (s.IDMappings != nil && len(s.IDMappings.GIDMap) > 0) { 85 mappingFound := false 86 for _, r := range s.IDMappings.GIDMap { 87 if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size { 88 mappingFound = true 89 break 90 } 91 } 92 if !mappingFound { 93 gid5Available = false 94 } 95 96 } 97 if !gid5Available { 98 // If we have no GID mappings, the gid=5 default option would fail, so drop it. 99 g.RemoveMount("/dev/pts") 100 devPts := spec.Mount{ 101 Destination: "/dev/pts", 102 Type: "devpts", 103 Source: "devpts", 104 Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, 105 } 106 g.AddMount(devPts) 107 } 108 109 if inUserNS && s.IpcNS.IsHost() { 110 g.RemoveMount("/dev/mqueue") 111 devMqueue := spec.Mount{ 112 Destination: "/dev/mqueue", 113 Type: "bind", // constant ? 114 Source: "/dev/mqueue", 115 Options: []string{"bind", "nosuid", "noexec", "nodev"}, 116 } 117 g.AddMount(devMqueue) 118 } 119 if inUserNS && s.PidNS.IsHost() { 120 g.RemoveMount("/proc") 121 procMount := spec.Mount{ 122 Destination: "/proc", 123 Type: createconfig.TypeBind, 124 Source: "/proc", 125 Options: []string{"rbind", "nosuid", "noexec", "nodev"}, 126 } 127 g.AddMount(procMount) 128 } 129 130 if addCgroup { 131 cgroupMnt := spec.Mount{ 132 Destination: "/sys/fs/cgroup", 133 Type: "cgroup", 134 Source: "cgroup", 135 Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm}, 136 } 137 g.AddMount(cgroupMnt) 138 } 139 g.SetProcessCwd(s.WorkDir) 140 g.SetProcessArgs(s.Command) 141 g.SetProcessTerminal(s.Terminal) 142 143 for key, val := range s.Annotations { 144 g.AddAnnotation(key, val) 145 } 146 g.AddProcessEnv("container", "podman") 147 148 g.Config.Linux.Resources = s.ResourceLimits 149 150 // Devices 151 if s.Privileged { 152 // If privileged, we need to add all the host devices to the 153 // spec. We do not add the user provided ones because we are 154 // already adding them all. 155 if err := createconfig.AddPrivilegedDevices(&g); err != nil { 156 return nil, err 157 } 158 } else { 159 for _, device := range s.Devices { 160 if err := createconfig.DevicesFromPath(&g, device.Path); err != nil { 161 return nil, err 162 } 163 } 164 } 165 166 // SECURITY OPTS 167 g.SetProcessNoNewPrivileges(s.NoNewPrivileges) 168 169 if !s.Privileged { 170 g.SetProcessApparmorProfile(s.ApparmorProfile) 171 } 172 173 createconfig.BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g) 174 175 for name, val := range s.Env { 176 g.AddProcessEnv(name, val) 177 } 178 179 // TODO rlimits and ulimits needs further refinement by someone more 180 // familiar with the code. 181 //if err := addRlimits(config, &g); err != nil { 182 // return nil, err 183 //} 184 185 // NAMESPACES 186 187 if err := pidConfigureGenerator(s, &g); err != nil { 188 return nil, err 189 } 190 191 if err := userConfigureGenerator(s, &g); err != nil { 192 return nil, err 193 } 194 195 if err := networkConfigureGenerator(s, &g); err != nil { 196 return nil, err 197 } 198 199 if err := utsConfigureGenerator(s, &g, rt); err != nil { 200 return nil, err 201 } 202 203 if err := ipcConfigureGenerator(s, &g); err != nil { 204 return nil, err 205 } 206 207 if err := cgroupConfigureGenerator(s, &g); err != nil { 208 return nil, err 209 } 210 configSpec := g.Config 211 212 if err := securityConfigureGenerator(s, &g, newImage); err != nil { 213 return nil, err 214 } 215 216 // BIND MOUNTS 217 configSpec.Mounts = createconfig.SupercedeUserMounts(s.Mounts, configSpec.Mounts) 218 // Process mounts to ensure correct options 219 if err := createconfig.InitFSMounts(configSpec.Mounts); err != nil { 220 return nil, err 221 } 222 223 // Add annotations 224 if configSpec.Annotations == nil { 225 configSpec.Annotations = make(map[string]string) 226 } 227 228 // TODO cidfile is not in specgen; when wiring up cli, we will need to move this out of here 229 // leaving as a reminder 230 //if config.CidFile != "" { 231 // configSpec.Annotations[libpod.InspectAnnotationCIDFile] = config.CidFile 232 //} 233 234 if s.Remove { 235 configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue 236 } else { 237 configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse 238 } 239 240 if len(s.VolumesFrom) > 0 { 241 configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",") 242 } 243 244 if s.Privileged { 245 configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue 246 } else { 247 configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse 248 } 249 250 // TODO Init might not make it into the specgen and therefore is not available here. We should deal 251 // with this when we wire up the CLI; leaving as a reminder 252 //if s.Init { 253 // configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue 254 //} else { 255 // configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse 256 //} 257 258 return configSpec, nil 259 }