diff options
author | Doug Rabson <dfr@rabson.org> | 2022-08-26 08:33:18 +0100 |
---|---|---|
committer | Doug Rabson <dfr@rabson.org> | 2022-08-29 13:06:15 +0100 |
commit | 0166feef2a0205967eb95867a7d953f9171b27b7 (patch) | |
tree | b4869e6da0f59f37f9a480f629e37e157beaef66 /pkg/specgen/generate/security_linux.go | |
parent | ac7f4ebb6a1fd3afbaea5e5ede9da0e341c4cdfe (diff) | |
download | podman-0166feef2a0205967eb95867a7d953f9171b27b7.tar.gz podman-0166feef2a0205967eb95867a7d953f9171b27b7.tar.bz2 podman-0166feef2a0205967eb95867a7d953f9171b27b7.zip |
specgen/generate: Move security.go to security_linux.go and add stubs
The security features (selinux, apparmor, capabilities) are linux
specific.
[NO NEW TESTS NEEDED]
Signed-off-by: Doug Rabson <dfr@rabson.org>
Diffstat (limited to 'pkg/specgen/generate/security_linux.go')
-rw-r--r-- | pkg/specgen/generate/security_linux.go | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/pkg/specgen/generate/security_linux.go b/pkg/specgen/generate/security_linux.go new file mode 100644 index 000000000..aacefcbac --- /dev/null +++ b/pkg/specgen/generate/security_linux.go @@ -0,0 +1,265 @@ +package generate + +import ( + "fmt" + "strings" + + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/apparmor" + "github.com/containers/common/pkg/capabilities" + "github.com/containers/common/pkg/config" + cutil "github.com/containers/common/pkg/util" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/specgen" + "github.com/containers/podman/v4/pkg/util" + "github.com/opencontainers/runtime-tools/generate" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/sirupsen/logrus" +) + +// setLabelOpts sets the label options of the SecurityConfig according to the +// input. +func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { + if !runtime.EnableLabeling() || s.Privileged { + s.SelinuxOpts = label.DisableSecOpt() + return nil + } + + var labelOpts []string + if pidConfig.IsHost() { + labelOpts = append(labelOpts, label.DisableSecOpt()...) + } else if pidConfig.IsContainer() { + ctr, err := runtime.LookupContainer(pidConfig.Value) + if err != nil { + return fmt.Errorf("container %q not found: %w", pidConfig.Value, err) + } + secopts, err := label.DupSecOpt(ctr.ProcessLabel()) + if err != nil { + return fmt.Errorf("failed to duplicate label %q : %w", ctr.ProcessLabel(), err) + } + labelOpts = append(labelOpts, secopts...) + } + + if ipcConfig.IsHost() { + labelOpts = append(labelOpts, label.DisableSecOpt()...) + } else if ipcConfig.IsContainer() { + ctr, err := runtime.LookupContainer(ipcConfig.Value) + if err != nil { + return fmt.Errorf("container %q not found: %w", ipcConfig.Value, err) + } + secopts, err := label.DupSecOpt(ctr.ProcessLabel()) + if err != nil { + return fmt.Errorf("failed to duplicate label %q : %w", ctr.ProcessLabel(), err) + } + labelOpts = append(labelOpts, secopts...) + } + + s.SelinuxOpts = append(s.SelinuxOpts, labelOpts...) + return nil +} + +func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Generator) error { + hasProfile := len(s.ApparmorProfile) > 0 + if !apparmor.IsEnabled() { + if hasProfile && s.ApparmorProfile != "unconfined" { + return fmt.Errorf("apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile) + } + return nil + } + // If privileged and caller did not specify apparmor profiles return + if s.Privileged && !hasProfile { + return nil + } + if !hasProfile { + s.ApparmorProfile = rtc.Containers.ApparmorProfile + } + if len(s.ApparmorProfile) > 0 { + g.SetProcessApparmorProfile(s.ApparmorProfile) + } + + return nil +} + +func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *libimage.Image, rtc *config.Config) error { + var ( + caplist []string + err error + ) + // HANDLE CAPABILITIES + // NOTE: Must happen before SECCOMP + if s.Privileged { + g.SetupPrivileged(true) + caplist, err = capabilities.BoundingSet() + if err != nil { + return err + } + } else { + mergedCaps, err := capabilities.MergeCapabilities(rtc.Containers.DefaultCapabilities, s.CapAdd, s.CapDrop) + if err != nil { + return err + } + boundingSet, err := capabilities.BoundingSet() + if err != nil { + return err + } + boundingCaps := make(map[string]interface{}) + for _, b := range boundingSet { + boundingCaps[b] = b + } + for _, c := range mergedCaps { + if _, ok := boundingCaps[c]; ok { + caplist = append(caplist, c) + } + } + + privCapsRequired := []string{} + + // If the container image specifies an label with a + // capabilities.ContainerImageLabel then split the comma separated list + // of capabilities and record them. This list indicates the only + // capabilities, required to run the container. + var capsRequiredRequested []string + for key, val := range s.Labels { + if cutil.StringInSlice(key, capabilities.ContainerImageLabels) { + capsRequiredRequested = strings.Split(val, ",") + } + } + if !s.Privileged && len(capsRequiredRequested) > 0 { + // Pass capRequiredRequested in CapAdd field to normalize capabilities names + capsRequired, err := capabilities.MergeCapabilities(nil, capsRequiredRequested, nil) + if err != nil { + return fmt.Errorf("capabilities requested by user or image are not valid: %q: %w", strings.Join(capsRequired, ","), err) + } + // Verify all capRequired are in the capList + for _, cap := range capsRequired { + if !cutil.StringInSlice(cap, caplist) { + privCapsRequired = append(privCapsRequired, cap) + } + } + if len(privCapsRequired) == 0 { + caplist = capsRequired + } else { + logrus.Errorf("Capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapsRequired, ",")) + } + } + } + + configSpec := g.Config + configSpec.Process.Capabilities.Ambient = []string{} + + // Always unset the inheritable capabilities similarly to what the Linux kernel does + // They are used only when using capabilities with uid != 0. + configSpec.Process.Capabilities.Inheritable = []string{} + configSpec.Process.Capabilities.Bounding = caplist + + user := strings.Split(s.User, ":")[0] + + if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" { + configSpec.Process.Capabilities.Effective = caplist + configSpec.Process.Capabilities.Permitted = caplist + } else { + mergedCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) + if err != nil { + return fmt.Errorf("capabilities requested by user are not valid: %q: %w", strings.Join(s.CapAdd, ","), err) + } + boundingSet, err := capabilities.BoundingSet() + if err != nil { + return err + } + boundingCaps := make(map[string]interface{}) + for _, b := range boundingSet { + boundingCaps[b] = b + } + var userCaps []string + for _, c := range mergedCaps { + if _, ok := boundingCaps[c]; ok { + userCaps = append(userCaps, c) + } + } + configSpec.Process.Capabilities.Effective = userCaps + configSpec.Process.Capabilities.Permitted = userCaps + + // Ambient capabilities were added to Linux 4.3. Set ambient + // capabilities only when the kernel supports them. + if supportAmbientCapabilities() { + configSpec.Process.Capabilities.Ambient = userCaps + configSpec.Process.Capabilities.Inheritable = userCaps + } + } + + g.SetProcessNoNewPrivileges(s.NoNewPrivileges) + + if err := setupApparmor(s, rtc, g); err != nil { + return err + } + + // HANDLE SECCOMP + if s.SeccompProfilePath != "unconfined" { + seccompConfig, err := getSeccompConfig(s, configSpec, newImage) + if err != nil { + return err + } + configSpec.Linux.Seccomp = seccompConfig + } + + // Clear default Seccomp profile from Generator for unconfined containers + // and privileged containers which do not specify a seccomp profile. + if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == "" || s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) { + configSpec.Linux.Seccomp = nil + } + + g.SetRootReadonly(s.ReadOnlyFilesystem) + + noUseIPC := s.IpcNS.NSMode == specgen.FromContainer || s.IpcNS.NSMode == specgen.FromPod || s.IpcNS.NSMode == specgen.Host + noUseNet := s.NetNS.NSMode == specgen.FromContainer || s.NetNS.NSMode == specgen.FromPod || s.NetNS.NSMode == specgen.Host + noUseUTS := s.UtsNS.NSMode == specgen.FromContainer || s.UtsNS.NSMode == specgen.FromPod || s.UtsNS.NSMode == specgen.Host + + // Add default sysctls + defaultSysctls, err := util.ValidateSysctls(rtc.Sysctls()) + if err != nil { + return err + } + for sysctlKey, sysctlVal := range defaultSysctls { + // Ignore mqueue sysctls if --ipc=host + if noUseIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { + logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to %q", sysctlKey, sysctlVal, s.IpcNS.NSMode) + + continue + } + + // Ignore net sysctls if --net=host + if noUseNet && strings.HasPrefix(sysctlKey, "net.") { + logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctlKey, sysctlVal) + continue + } + + // Ignore uts sysctls if --uts=host + if noUseUTS && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { + logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace set to host", sysctlKey, sysctlVal) + continue + } + + g.AddLinuxSysctl(sysctlKey, sysctlVal) + } + + for sysctlKey, sysctlVal := range s.Sysctl { + if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") { + return fmt.Errorf("sysctl %s=%s can't be set since IPC Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) + } + + // Ignore net sysctls if --net=host + if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") { + return fmt.Errorf("sysctl %s=%s can't be set since Network Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) + } + + // Ignore uts sysctls if --uts=host + if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { + return fmt.Errorf("sysctl %s=%s can't be set since UTS Namespace set to host: %w", sysctlKey, sysctlVal, define.ErrInvalidArg) + } + + g.AddLinuxSysctl(sysctlKey, sysctlVal) + } + + return nil +} |