diff options
Diffstat (limited to 'pkg/specgen')
-rw-r--r-- | pkg/specgen/container_validate.go | 6 | ||||
-rw-r--r-- | pkg/specgen/generate/config_linux.go | 139 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 14 | ||||
-rw-r--r-- | pkg/specgen/generate/namespaces.go | 41 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 10 | ||||
-rw-r--r-- | pkg/specgen/generate/pod_create.go | 9 | ||||
-rw-r--r-- | pkg/specgen/namespaces.go | 65 | ||||
-rw-r--r-- | pkg/specgen/namespaces_test.go | 25 | ||||
-rw-r--r-- | pkg/specgen/podspecgen.go | 5 | ||||
-rw-r--r-- | pkg/specgen/volumes.go | 14 |
10 files changed, 85 insertions, 243 deletions
diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index 532a2094f..5616a4511 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -183,10 +183,12 @@ func (s *SpecGenerator) Validate() error { } // Set defaults if network info is not provided - if s.NetNS.NSMode == "" { - s.NetNS.NSMode = Bridge + // when we are rootless we default to slirp4netns + if s.NetNS.IsPrivate() || s.NetNS.IsDefault() { if rootless.IsRootless() { s.NetNS.NSMode = Slirp + } else { + s.NetNS.NSMode = Bridge } } if err := validateNetNS(&s.NetNS); err != nil { diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index ed2e5408d..4c3748e67 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -3,7 +3,6 @@ package generate import ( "fmt" "io/fs" - "io/ioutil" "os" "path" "path/filepath" @@ -11,6 +10,7 @@ import ( "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/rootless" + "github.com/containers/podman/v4/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" @@ -18,56 +18,6 @@ import ( "golang.org/x/sys/unix" ) -var ( - errNotADevice = errors.New("not a device node") -) - -func addPrivilegedDevices(g *generate.Generator) error { - hostDevices, err := getDevices("/dev") - if err != nil { - return err - } - g.ClearLinuxDevices() - - if rootless.IsRootless() { - mounts := make(map[string]interface{}) - for _, m := range g.Mounts() { - mounts[m.Destination] = true - } - newMounts := []spec.Mount{} - for _, d := range hostDevices { - devMnt := spec.Mount{ - Destination: d.Path, - Type: define.TypeBind, - Source: d.Path, - Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"}, - } - if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") { - continue - } - if _, found := mounts[d.Path]; found { - continue - } - newMounts = append(newMounts, devMnt) - } - g.Config.Mounts = append(newMounts, g.Config.Mounts...) - if g.Config.Linux.Resources != nil { - g.Config.Linux.Resources.Devices = nil - } - } else { - for _, d := range hostDevices { - g.AddDevice(d) - } - // Add resources device - need to clear the existing one first. - if g.Config.Linux.Resources != nil { - g.Config.Linux.Resources.Devices = nil - } - g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm") - } - - return nil -} - // DevicesFromPath computes a list of devices func DevicesFromPath(g *generate.Generator, devicePath string) error { devs := strings.Split(devicePath, ":") @@ -174,60 +124,12 @@ func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, mask, unmask } } -// based on getDevices from runc (libcontainer/devices/devices.go) -func getDevices(path string) ([]spec.LinuxDevice, error) { - files, err := ioutil.ReadDir(path) - if err != nil { - if rootless.IsRootless() && os.IsPermission(err) { - return nil, nil - } - return nil, err - } - out := []spec.LinuxDevice{} - for _, f := range files { - switch { - case f.IsDir(): - switch f.Name() { - // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 - case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts": - continue - default: - sub, err := getDevices(filepath.Join(path, f.Name())) - if err != nil { - return nil, err - } - if sub != nil { - out = append(out, sub...) - } - continue - } - case f.Name() == "console": - continue - case f.Mode()&os.ModeSymlink != 0: - continue - } - - device, err := deviceFromPath(filepath.Join(path, f.Name())) - if err != nil { - if err == errNotADevice { - continue - } - if os.IsNotExist(err) { - continue - } - return nil, err - } - out = append(out, *device) - } - return out, nil -} - func addDevice(g *generate.Generator, device string) error { src, dst, permissions, err := ParseDevice(device) if err != nil { return err } - dev, err := deviceFromPath(src) + dev, err := util.DeviceFromPath(src) if err != nil { return errors.Wrapf(err, "%s is not a valid device", src) } @@ -316,43 +218,6 @@ func IsValidDeviceMode(mode string) bool { return true } -// Copied from github.com/opencontainers/runc/libcontainer/devices -// Given the path to a device look up the information about a linux device -func deviceFromPath(path string) (*spec.LinuxDevice, error) { - var stat unix.Stat_t - err := unix.Lstat(path, &stat) - if err != nil { - return nil, err - } - var ( - devType string - mode = stat.Mode - devNumber = uint64(stat.Rdev) // nolint: unconvert - m = os.FileMode(mode) - ) - - switch { - case mode&unix.S_IFBLK == unix.S_IFBLK: - devType = "b" - case mode&unix.S_IFCHR == unix.S_IFCHR: - devType = "c" - case mode&unix.S_IFIFO == unix.S_IFIFO: - devType = "p" - default: - return nil, errNotADevice - } - - return &spec.LinuxDevice{ - Type: devType, - Path: path, - FileMode: &m, - UID: &stat.Uid, - GID: &stat.Gid, - Major: int64(unix.Major(devNumber)), - Minor: int64(unix.Minor(devNumber)), - }, nil -} - func supportAmbientCapabilities() bool { err := unix.Prctl(unix.PR_CAP_AMBIENT, unix.PR_CAP_AMBIENT_IS_SET, 0, 0, 0) return err == nil diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 04e24d625..7faf13465 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -278,6 +278,10 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l options = append(options, libpod.WithPasswdEntry(s.PasswdEntry)) } + if s.Privileged { + options = append(options, libpod.WithMountAllDevices()) + } + useSystemd := false switch s.Systemd { case "always": @@ -542,6 +546,16 @@ func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtim infraConf := infra.Config() infraSpec := infraConf.Spec + // need to set compatOptions to the currently filled specgenOptions so we do not overwrite + compatibleOptions.CapAdd = append(compatibleOptions.CapAdd, s.CapAdd...) + compatibleOptions.CapDrop = append(compatibleOptions.CapDrop, s.CapDrop...) + compatibleOptions.HostDeviceList = append(compatibleOptions.HostDeviceList, s.HostDeviceList...) + compatibleOptions.ImageVolumes = append(compatibleOptions.ImageVolumes, s.ImageVolumes...) + compatibleOptions.Mounts = append(compatibleOptions.Mounts, s.Mounts...) + compatibleOptions.OverlayVolumes = append(compatibleOptions.OverlayVolumes, s.OverlayVolumes...) + compatibleOptions.SelinuxOpts = append(compatibleOptions.SelinuxOpts, s.SelinuxOpts...) + compatibleOptions.Volumes = append(compatibleOptions.Volumes, s.Volumes...) + compatByte, err := json.Marshal(compatibleOptions) if err != nil { return nil, nil, nil, err diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 37d561ec2..4224d16ce 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -19,6 +19,8 @@ import ( "github.com/sirupsen/logrus" ) +const host = "host" + // Get the default namespace mode for any given namespace type. func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) (specgen.Namespace, error) { // The default for most is private @@ -33,16 +35,38 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) podMode := false switch { case nsType == "pid" && pod.SharesPID(): + if pod.NamespaceMode(spec.PIDNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "ipc" && pod.SharesIPC(): + if pod.NamespaceMode(spec.IPCNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "uts" && pod.SharesUTS(): + if pod.NamespaceMode(spec.UTSNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "user" && pod.SharesUser(): + // user does not need a special check for host, this is already validated on pod creation + // if --userns=host then pod.SharesUser == false podMode = true case nsType == "net" && pod.SharesNet(): + if pod.NetworkMode() == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true case nsType == "cgroup" && pod.SharesCgroup(): + if pod.NamespaceMode(spec.CgroupNamespace) == host { + toReturn.NSMode = specgen.Host + return toReturn, nil + } podMode = true } if podMode { @@ -236,10 +260,12 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. toReturn = append(toReturn, libpod.WithCgroupsMode(s.CgroupsMode)) } - // Net - // TODO validate CNINetworks, StaticIP, StaticIPv6 are only set if we - // are in bridge mode. postConfigureNetNS := !s.UserNS.IsHost() + // when we are rootless we default to slirp4netns + if rootless.IsRootless() && (s.NetNS.IsPrivate() || s.NetNS.IsDefault()) { + s.NetNS.NSMode = specgen.Slirp + } + switch s.NetNS.NSMode { case specgen.FromPod: if pod == nil || infraCtr == nil { @@ -262,9 +288,7 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod. val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) } toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil)) - case specgen.Private: - fallthrough - case specgen.Bridge: + case specgen.Bridge, specgen.Private, specgen.Default: portMappings, expose, err := createPortMappings(s, imageData) if err != nil { return nil, err @@ -488,10 +512,7 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio case "cgroup": options = append(options, libpod.WithPodCgroup()) case "net": - // share the netns setting with other containers in the pod only when it is not set to host - if !netnsIsHost { - options = append(options, libpod.WithPodNet()) - } + options = append(options, libpod.WithPodNet()) case "mnt": return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level") case "pid": diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index dda2de6e4..716960024 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -337,14 +337,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt } var userDevices []spec.LinuxDevice - if s.Privileged { - // If privileged, we need to add all the host devices to the - // spec. We do not add the user provided ones because we are - // already adding them all. - if err := addPrivilegedDevices(&g); err != nil { - return nil, err - } - } else { + + if !s.Privileged { // add default devices from containers.conf for _, device := range rtc.Containers.Devices { if err = DevicesFromPath(&g, device); err != nil { diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 5b7bb2b57..d4f281a11 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -141,6 +141,9 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { case specgen.Bridge: p.InfraContainerSpec.NetNS.NSMode = specgen.Bridge logrus.Debugf("Pod using bridge network mode") + case specgen.Private: + p.InfraContainerSpec.NetNS.NSMode = specgen.Private + logrus.Debugf("Pod will use default network mode") case specgen.Host: logrus.Debugf("Pod will use host networking") if len(p.InfraContainerSpec.PortMappings) > 0 || @@ -151,15 +154,15 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { p.InfraContainerSpec.NetNS.NSMode = specgen.Host case specgen.Slirp: logrus.Debugf("Pod will use slirp4netns") - if p.InfraContainerSpec.NetNS.NSMode != "host" { + if p.InfraContainerSpec.NetNS.NSMode != specgen.Host { p.InfraContainerSpec.NetworkOptions = p.NetworkOptions - p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("slirp4netns") + p.InfraContainerSpec.NetNS.NSMode = specgen.Slirp } case specgen.NoNetwork: logrus.Debugf("Pod will not use networking") if len(p.InfraContainerSpec.PortMappings) > 0 || len(p.InfraContainerSpec.Networks) > 0 || - p.InfraContainerSpec.NetNS.NSMode == "host" { + p.InfraContainerSpec.NetNS.NSMode == specgen.Host { return nil, errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") } p.InfraContainerSpec.NetNS.NSMode = specgen.NoNetwork diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index 5a3b94ca4..f1343f6e2 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -10,7 +10,6 @@ import ( "github.com/containers/common/pkg/cgroups" cutil "github.com/containers/common/pkg/util" "github.com/containers/podman/v4/libpod/define" - "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" "github.com/containers/storage" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -319,62 +318,6 @@ func ParseUserNamespace(ns string) (Namespace, error) { return ParseNamespace(ns) } -// ParseNetworkNamespace parses a network namespace specification in string -// form. -// Returns a namespace and (optionally) a list of CNI networks to join. -func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, map[string]types.PerNetworkOptions, error) { - toReturn := Namespace{} - networks := make(map[string]types.PerNetworkOptions) - // Net defaults to Slirp on rootless - switch { - case ns == string(Slirp), strings.HasPrefix(ns, string(Slirp)+":"): - toReturn.NSMode = Slirp - case ns == string(FromPod): - toReturn.NSMode = FromPod - case ns == "" || ns == string(Default) || ns == string(Private): - if rootless.IsRootless() { - if rootlessDefaultCNI { - toReturn.NSMode = Bridge - } else { - toReturn.NSMode = Slirp - } - } else { - toReturn.NSMode = Bridge - } - case ns == string(Bridge): - toReturn.NSMode = Bridge - case ns == string(NoNetwork): - toReturn.NSMode = NoNetwork - case ns == string(Host): - toReturn.NSMode = Host - case strings.HasPrefix(ns, "ns:"): - split := strings.SplitN(ns, ":", 2) - if len(split) != 2 { - return toReturn, nil, errors.Errorf("must provide a path to a namespace when specifying \"ns:\"") - } - toReturn.NSMode = Path - toReturn.Value = split[1] - case strings.HasPrefix(ns, string(FromContainer)+":"): - split := strings.SplitN(ns, ":", 2) - if len(split) != 2 { - return toReturn, nil, errors.Errorf("must provide name or ID or a container when specifying \"container:\"") - } - toReturn.NSMode = FromContainer - toReturn.Value = split[1] - default: - // Assume we have been given a list of CNI networks. - // Which only works in bridge mode, so set that. - networkList := strings.Split(ns, ",") - for _, net := range networkList { - networks[net] = types.PerNetworkOptions{} - } - - toReturn.NSMode = Bridge - } - - return toReturn, networks, nil -} - // ParseNetworkFlag parses a network string slice into the network options // If the input is nil or empty it will use the default setting from containers.conf func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetworkOptions, map[string][]string, error) { @@ -400,13 +343,7 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork case ns == string(FromPod): toReturn.NSMode = FromPod case ns == "" || ns == string(Default) || ns == string(Private): - // Net defaults to Slirp on rootless - if rootless.IsRootless() { - toReturn.NSMode = Slirp - break - } - // if root we use bridge - fallthrough + toReturn.NSMode = Private case ns == string(Bridge), strings.HasPrefix(ns, string(Bridge)+":"): toReturn.NSMode = Bridge parts := strings.SplitN(ns, ":", 2) diff --git a/pkg/specgen/namespaces_test.go b/pkg/specgen/namespaces_test.go index 368c92bd5..d03a6d032 100644 --- a/pkg/specgen/namespaces_test.go +++ b/pkg/specgen/namespaces_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/containers/common/libnetwork/types" - "github.com/containers/podman/v4/pkg/rootless" "github.com/stretchr/testify/assert" ) @@ -17,14 +16,6 @@ func parsMacNoErr(mac string) types.HardwareAddr { func TestParseNetworkFlag(t *testing.T) { // root and rootless have different defaults defaultNetName := "default" - defaultNetworks := map[string]types.PerNetworkOptions{ - defaultNetName: {}, - } - defaultNsMode := Namespace{NSMode: Bridge} - if rootless.IsRootless() { - defaultNsMode = Namespace{NSMode: Slirp} - defaultNetworks = map[string]types.PerNetworkOptions{} - } tests := []struct { name string @@ -37,26 +28,26 @@ func TestParseNetworkFlag(t *testing.T) { { name: "empty input", args: nil, - nsmode: defaultNsMode, - networks: defaultNetworks, + nsmode: Namespace{NSMode: Private}, + networks: map[string]types.PerNetworkOptions{}, }, { name: "empty string as input", args: []string{}, - nsmode: defaultNsMode, - networks: defaultNetworks, + nsmode: Namespace{NSMode: Private}, + networks: map[string]types.PerNetworkOptions{}, }, { name: "default mode", args: []string{"default"}, - nsmode: defaultNsMode, - networks: defaultNetworks, + nsmode: Namespace{NSMode: Private}, + networks: map[string]types.PerNetworkOptions{}, }, { name: "private mode", args: []string{"private"}, - nsmode: defaultNsMode, - networks: defaultNetworks, + nsmode: Namespace{NSMode: Private}, + networks: map[string]types.PerNetworkOptions{}, }, { name: "bridge mode", diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 603506241..777097ac5 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -4,6 +4,7 @@ import ( "net" "github.com/containers/common/libnetwork/types" + storageTypes "github.com/containers/storage/types" spec "github.com/opencontainers/runtime-spec/specs-go" ) @@ -222,6 +223,10 @@ type PodResourceConfig struct { type PodSecurityConfig struct { SecurityOpt []string `json:"security_opt,omitempty"` + // IDMappings are UID and GID mappings that will be used by user + // namespaces. + // Required if UserNS is private. + IDMappings *storageTypes.IDMappingOptions `json:"idmappings,omitempty"` } // NewPodSpecGenerator creates a new pod spec diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index b26666df3..a7a1022b0 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -97,6 +97,8 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na // This is not a named volume overlayFlag := false chownFlag := false + upperDirFlag := false + workDirFlag := false for _, o := range options { if o == "O" { overlayFlag = true @@ -105,8 +107,16 @@ func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*Na if strings.Contains(joinedOpts, "U") { chownFlag = true } - - if len(options) > 2 || (len(options) == 2 && !chownFlag) { + if strings.Contains(joinedOpts, "upperdir") { + upperDirFlag = true + } + if strings.Contains(joinedOpts, "workdir") { + workDirFlag = true + } + if (workDirFlag && !upperDirFlag) || (!workDirFlag && upperDirFlag) { + return nil, nil, nil, errors.New("must set both `upperdir` and `workdir`") + } + if len(options) > 2 && !(len(options) == 3 && upperDirFlag && workDirFlag) || (len(options) == 2 && !chownFlag) { return nil, nil, nil, errors.New("can't use 'O' with other options") } } |