github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/domain/infra/runtime_libpod.go (about) 1 // +build !remote 2 3 package infra 4 5 import ( 6 "context" 7 "fmt" 8 "os" 9 "os/signal" 10 "sync" 11 12 "github.com/containers/podman/v2/cmd/podman/utils" 13 "github.com/containers/podman/v2/libpod" 14 "github.com/containers/podman/v2/pkg/cgroups" 15 "github.com/containers/podman/v2/pkg/domain/entities" 16 "github.com/containers/podman/v2/pkg/namespaces" 17 "github.com/containers/podman/v2/pkg/rootless" 18 "github.com/containers/storage" 19 "github.com/containers/storage/pkg/idtools" 20 "github.com/pkg/errors" 21 "github.com/sirupsen/logrus" 22 flag "github.com/spf13/pflag" 23 ) 24 25 var ( 26 // runtimeSync only guards the non-specialized runtime 27 runtimeSync sync.Once 28 // The default GetRuntime() always returns the same object and error 29 runtimeLib *libpod.Runtime 30 runtimeErr error 31 ) 32 33 type engineOpts struct { 34 name string 35 renumber bool 36 migrate bool 37 noStore bool 38 withFDS bool 39 config *entities.PodmanConfig 40 } 41 42 // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers 43 func GetRuntimeMigrate(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig, newRuntime string) (*libpod.Runtime, error) { 44 return getRuntime(ctx, fs, &engineOpts{ 45 name: newRuntime, 46 renumber: false, 47 migrate: true, 48 noStore: false, 49 withFDS: true, 50 config: cfg, 51 }) 52 } 53 54 // GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify 55 func GetRuntimeDisableFDs(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) { 56 return getRuntime(ctx, fs, &engineOpts{ 57 renumber: false, 58 migrate: false, 59 noStore: false, 60 withFDS: false, 61 config: cfg, 62 }) 63 } 64 65 // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber 66 func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) { 67 return getRuntime(ctx, fs, &engineOpts{ 68 renumber: true, 69 migrate: false, 70 noStore: false, 71 withFDS: true, 72 config: cfg, 73 }) 74 } 75 76 // GetRuntime generates a new libpod runtime configured by command line options 77 func GetRuntime(ctx context.Context, flags *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) { 78 runtimeSync.Do(func() { 79 runtimeLib, runtimeErr = getRuntime(ctx, flags, &engineOpts{ 80 renumber: false, 81 migrate: false, 82 noStore: false, 83 withFDS: true, 84 config: cfg, 85 }) 86 }) 87 return runtimeLib, runtimeErr 88 } 89 90 // GetRuntimeNoStore generates a new libpod runtime configured by command line options 91 func GetRuntimeNoStore(ctx context.Context, fs *flag.FlagSet, cfg *entities.PodmanConfig) (*libpod.Runtime, error) { 92 return getRuntime(ctx, fs, &engineOpts{ 93 renumber: false, 94 migrate: false, 95 noStore: true, 96 withFDS: true, 97 config: cfg, 98 }) 99 } 100 101 func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) { 102 options := []libpod.RuntimeOption{} 103 storageOpts := storage.StoreOptions{} 104 cfg := opts.config 105 106 storageSet := false 107 108 uidmapFlag := fs.Lookup("uidmap") 109 gidmapFlag := fs.Lookup("gidmap") 110 subuidname := fs.Lookup("subuidname") 111 subgidname := fs.Lookup("subgidname") 112 if (uidmapFlag != nil && gidmapFlag != nil && subuidname != nil && subgidname != nil) && 113 (uidmapFlag.Changed || gidmapFlag.Changed || subuidname.Changed || subgidname.Changed) { 114 userns, _ := fs.GetString("userns") 115 uidmapVal, _ := fs.GetStringSlice("uidmap") 116 gidmapVal, _ := fs.GetStringSlice("gidmap") 117 subuidVal, _ := fs.GetString("subuidname") 118 subgidVal, _ := fs.GetString("subgidname") 119 mappings, err := ParseIDMapping(namespaces.UsernsMode(userns), uidmapVal, gidmapVal, subuidVal, subgidVal) 120 if err != nil { 121 return nil, err 122 } 123 storageOpts.UIDMap = mappings.UIDMap 124 storageOpts.GIDMap = mappings.GIDMap 125 126 storageSet = true 127 } 128 129 if fs.Changed("root") { 130 storageSet = true 131 storageOpts.GraphRoot = cfg.Engine.StaticDir 132 } 133 if fs.Changed("runroot") { 134 storageSet = true 135 storageOpts.RunRoot = cfg.Runroot 136 } 137 if len(storageOpts.RunRoot) > 50 { 138 return nil, errors.New("the specified runroot is longer than 50 characters") 139 } 140 if fs.Changed("storage-driver") { 141 storageSet = true 142 storageOpts.GraphDriverName = cfg.StorageDriver 143 // Overriding the default storage driver caused GraphDriverOptions from storage.conf to be ignored 144 storageOpts.GraphDriverOptions = []string{} 145 } 146 // This should always be checked after storage-driver is checked 147 if len(cfg.StorageOpts) > 0 { 148 storageSet = true 149 storageOpts.GraphDriverOptions = cfg.StorageOpts 150 } 151 if opts.migrate { 152 options = append(options, libpod.WithMigrate()) 153 if opts.name != "" { 154 options = append(options, libpod.WithMigrateRuntime(opts.name)) 155 } 156 } 157 158 if opts.renumber { 159 options = append(options, libpod.WithRenumber()) 160 } 161 162 if len(cfg.RuntimeFlags) > 0 { 163 runtimeFlags := []string{} 164 for _, arg := range cfg.RuntimeFlags { 165 runtimeFlags = append(runtimeFlags, "--"+arg) 166 } 167 options = append(options, libpod.WithRuntimeFlags(runtimeFlags)) 168 } 169 170 // Only set this if the user changes storage config on the command line 171 if storageSet { 172 options = append(options, libpod.WithStorageConfig(storageOpts)) 173 } 174 175 if !storageSet && opts.noStore { 176 options = append(options, libpod.WithNoStore()) 177 } 178 // TODO CLI flags for image config? 179 // TODO CLI flag for signature policy? 180 181 if len(cfg.Engine.Namespace) > 0 { 182 options = append(options, libpod.WithNamespace(cfg.Engine.Namespace)) 183 } 184 185 if fs.Changed("runtime") { 186 options = append(options, libpod.WithOCIRuntime(cfg.RuntimePath)) 187 } 188 189 if fs.Changed("conmon") { 190 options = append(options, libpod.WithConmonPath(cfg.ConmonPath)) 191 } 192 if fs.Changed("tmpdir") { 193 options = append(options, libpod.WithTmpDir(cfg.Engine.TmpDir)) 194 } 195 if fs.Changed("network-cmd-path") { 196 options = append(options, libpod.WithNetworkCmdPath(cfg.Engine.NetworkCmdPath)) 197 } 198 199 if fs.Changed("events-backend") { 200 options = append(options, libpod.WithEventsLogger(cfg.Engine.EventsLogger)) 201 } 202 203 if fs.Changed("cgroup-manager") { 204 options = append(options, libpod.WithCgroupManager(cfg.Engine.CgroupManager)) 205 } else { 206 unified, err := cgroups.IsCgroup2UnifiedMode() 207 if err != nil { 208 return nil, err 209 } 210 if rootless.IsRootless() && !unified { 211 options = append(options, libpod.WithCgroupManager("cgroupfs")) 212 } 213 } 214 215 // TODO flag to set libpod static dir? 216 // TODO flag to set libpod tmp dir? 217 218 if fs.Changed("cni-config-dir") { 219 options = append(options, libpod.WithCNIConfigDir(cfg.Network.NetworkConfigDir)) 220 } 221 if fs.Changed("default-mounts-file") { 222 options = append(options, libpod.WithDefaultMountsFile(cfg.Containers.DefaultMountsFile)) 223 } 224 if fs.Changed("hooks-dir") { 225 options = append(options, libpod.WithHooksDir(cfg.Engine.HooksDir...)) 226 } 227 if fs.Changed("registries-conf") { 228 options = append(options, libpod.WithRegistriesConf(cfg.RegistriesConf)) 229 } 230 231 // TODO flag to set CNI plugins dir? 232 233 if !opts.withFDS { 234 options = append(options, libpod.WithEnableSDNotify()) 235 } 236 return libpod.NewRuntime(ctx, options...) 237 } 238 239 // ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping 240 func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) { 241 options := storage.IDMappingOptions{ 242 HostUIDMapping: true, 243 HostGIDMapping: true, 244 } 245 246 if mode.IsAuto() { 247 var err error 248 options.HostUIDMapping = false 249 options.HostGIDMapping = false 250 options.AutoUserNs = true 251 opts, err := mode.GetAutoOptions() 252 if err != nil { 253 return nil, err 254 } 255 options.AutoUserNsOpts = *opts 256 return &options, nil 257 } 258 if mode.IsKeepID() { 259 if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 { 260 return nil, errors.New("cannot specify custom mappings with --userns=keep-id") 261 } 262 if len(subUIDMap) > 0 || len(subGIDMap) > 0 { 263 return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id") 264 } 265 if rootless.IsRootless() { 266 min := func(a, b int) int { 267 if a < b { 268 return a 269 } 270 return b 271 } 272 273 uid := rootless.GetRootlessUID() 274 gid := rootless.GetRootlessGID() 275 276 uids, gids, err := rootless.GetConfiguredMappings() 277 if err != nil { 278 return nil, errors.Wrapf(err, "cannot read mappings") 279 } 280 maxUID, maxGID := 0, 0 281 for _, u := range uids { 282 maxUID += u.Size 283 } 284 for _, g := range gids { 285 maxGID += g.Size 286 } 287 288 options.UIDMap, options.GIDMap = nil, nil 289 290 options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)}) 291 options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1}) 292 if maxUID > uid { 293 options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid}) 294 } 295 296 options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)}) 297 options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1}) 298 if maxGID > gid { 299 options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid}) 300 } 301 302 options.HostUIDMapping = false 303 options.HostGIDMapping = false 304 } 305 // Simply ignore the setting and do not setup an inner namespace for root as it is a no-op 306 return &options, nil 307 } 308 309 if subGIDMap == "" && subUIDMap != "" { 310 subGIDMap = subUIDMap 311 } 312 if subUIDMap == "" && subGIDMap != "" { 313 subUIDMap = subGIDMap 314 } 315 if len(gidMapSlice) == 0 && len(uidMapSlice) != 0 { 316 gidMapSlice = uidMapSlice 317 } 318 if len(uidMapSlice) == 0 && len(gidMapSlice) != 0 { 319 uidMapSlice = gidMapSlice 320 } 321 if len(uidMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 { 322 uidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())} 323 } 324 if len(gidMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 { 325 gidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())} 326 } 327 328 if subUIDMap != "" && subGIDMap != "" { 329 mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap) 330 if err != nil { 331 return nil, err 332 } 333 options.UIDMap = mappings.UIDs() 334 options.GIDMap = mappings.GIDs() 335 } 336 parsedUIDMap, err := idtools.ParseIDMap(uidMapSlice, "UID") 337 if err != nil { 338 return nil, err 339 } 340 parsedGIDMap, err := idtools.ParseIDMap(gidMapSlice, "GID") 341 if err != nil { 342 return nil, err 343 } 344 options.UIDMap = append(options.UIDMap, parsedUIDMap...) 345 options.GIDMap = append(options.GIDMap, parsedGIDMap...) 346 if len(options.UIDMap) > 0 { 347 options.HostUIDMapping = false 348 } 349 if len(options.GIDMap) > 0 { 350 options.HostGIDMapping = false 351 } 352 return &options, nil 353 } 354 355 // StartWatcher starts a new SIGHUP go routine for the current config. 356 func StartWatcher(rt *libpod.Runtime) { 357 // Setup the signal notifier 358 ch := make(chan os.Signal, 1) 359 signal.Notify(ch, utils.SIGHUP) 360 361 go func() { 362 for { 363 // Block until the signal is received 364 logrus.Debugf("waiting for SIGHUP to reload configuration") 365 <-ch 366 if err := rt.Reload(); err != nil { 367 logrus.Errorf("unable to reload configuration: %v", err) 368 continue 369 } 370 } 371 }() 372 373 logrus.Debugf("registered SIGHUP watcher for config") 374 }