github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/generate/security.go (about)

     1  package generate
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/containers/common/pkg/apparmor"
     7  	"github.com/containers/common/pkg/capabilities"
     8  	"github.com/containers/common/pkg/config"
     9  	"github.com/containers/podman/v2/libpod"
    10  	"github.com/containers/podman/v2/libpod/define"
    11  	"github.com/containers/podman/v2/libpod/image"
    12  	"github.com/containers/podman/v2/pkg/specgen"
    13  	"github.com/containers/podman/v2/pkg/util"
    14  	"github.com/opencontainers/runtime-tools/generate"
    15  	"github.com/opencontainers/selinux/go-selinux/label"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  // setLabelOpts sets the label options of the SecurityConfig according to the
    21  // input.
    22  func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error {
    23  	if !runtime.EnableLabeling() || s.Privileged {
    24  		s.SelinuxOpts = label.DisableSecOpt()
    25  		return nil
    26  	}
    27  
    28  	var labelOpts []string
    29  	if pidConfig.IsHost() {
    30  		labelOpts = append(labelOpts, label.DisableSecOpt()...)
    31  	} else if pidConfig.IsContainer() {
    32  		ctr, err := runtime.LookupContainer(pidConfig.Value)
    33  		if err != nil {
    34  			return errors.Wrapf(err, "container %q not found", pidConfig.Value)
    35  		}
    36  		secopts, err := label.DupSecOpt(ctr.ProcessLabel())
    37  		if err != nil {
    38  			return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
    39  		}
    40  		labelOpts = append(labelOpts, secopts...)
    41  	}
    42  
    43  	if ipcConfig.IsHost() {
    44  		labelOpts = append(labelOpts, label.DisableSecOpt()...)
    45  	} else if ipcConfig.IsContainer() {
    46  		ctr, err := runtime.LookupContainer(ipcConfig.Value)
    47  		if err != nil {
    48  			return errors.Wrapf(err, "container %q not found", ipcConfig.Value)
    49  		}
    50  		secopts, err := label.DupSecOpt(ctr.ProcessLabel())
    51  		if err != nil {
    52  			return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
    53  		}
    54  		labelOpts = append(labelOpts, secopts...)
    55  	}
    56  
    57  	s.SelinuxOpts = append(s.SelinuxOpts, labelOpts...)
    58  	return nil
    59  }
    60  
    61  func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Generator) error {
    62  	hasProfile := len(s.ApparmorProfile) > 0
    63  	if !apparmor.IsEnabled() {
    64  		if hasProfile && s.ApparmorProfile != "unconfined" {
    65  			return errors.Errorf("Apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile)
    66  		}
    67  		return nil
    68  	}
    69  	// If privileged and caller did not specify apparmor profiles return
    70  	if s.Privileged && !hasProfile {
    71  		return nil
    72  	}
    73  	if !hasProfile {
    74  		s.ApparmorProfile = rtc.Containers.ApparmorProfile
    75  	}
    76  	if len(s.ApparmorProfile) > 0 {
    77  		g.SetProcessApparmorProfile(s.ApparmorProfile)
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image, rtc *config.Config) error {
    84  	var (
    85  		caplist []string
    86  		err     error
    87  	)
    88  	// HANDLE CAPABILITIES
    89  	// NOTE: Must happen before SECCOMP
    90  	if s.Privileged {
    91  		g.SetupPrivileged(true)
    92  		caplist = capabilities.AllCapabilities()
    93  	} else {
    94  		caplist, err = capabilities.MergeCapabilities(rtc.Containers.DefaultCapabilities, s.CapAdd, s.CapDrop)
    95  		if err != nil {
    96  			return err
    97  		}
    98  
    99  		privCapsRequired := []string{}
   100  
   101  		// If the container image specifies an label with a
   102  		// capabilities.ContainerImageLabel then split the comma separated list
   103  		// of capabilities and record them.  This list indicates the only
   104  		// capabilities, required to run the container.
   105  		var capsRequiredRequested []string
   106  		for key, val := range s.Labels {
   107  			if util.StringInSlice(key, capabilities.ContainerImageLabels) {
   108  				capsRequiredRequested = strings.Split(val, ",")
   109  			}
   110  		}
   111  		if !s.Privileged && len(capsRequiredRequested) > 0 {
   112  
   113  			// Pass capRequiredRequested in CapAdd field to normalize capabilities names
   114  			capsRequired, err := capabilities.MergeCapabilities(nil, capsRequiredRequested, nil)
   115  			if err != nil {
   116  				return errors.Wrapf(err, "capabilities requested by user or image are not valid: %q", strings.Join(capsRequired, ","))
   117  			} else {
   118  				// Verify all capRequiered are in the capList
   119  				for _, cap := range capsRequired {
   120  					if !util.StringInSlice(cap, caplist) {
   121  						privCapsRequired = append(privCapsRequired, cap)
   122  					}
   123  				}
   124  			}
   125  			if len(privCapsRequired) == 0 {
   126  				caplist = capsRequired
   127  			} else {
   128  				logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapsRequired, ","))
   129  			}
   130  		}
   131  	}
   132  
   133  	configSpec := g.Config
   134  	configSpec.Process.Capabilities.Ambient = []string{}
   135  	configSpec.Process.Capabilities.Bounding = caplist
   136  	configSpec.Process.Capabilities.Inheritable = caplist
   137  
   138  	user := strings.Split(s.User, ":")[0]
   139  
   140  	if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" {
   141  		configSpec.Process.Capabilities.Effective = caplist
   142  		configSpec.Process.Capabilities.Permitted = caplist
   143  	} else {
   144  		userCaps, err := capabilities.NormalizeCapabilities(s.CapAdd)
   145  		if err != nil {
   146  			return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ","))
   147  		}
   148  		configSpec.Process.Capabilities.Effective = userCaps
   149  		configSpec.Process.Capabilities.Permitted = userCaps
   150  
   151  		// Ambient capabilities were added to Linux 4.3.  Set ambient
   152  		// capabilities only when the kernel supports them.
   153  		if supportAmbientCapabilities() {
   154  			configSpec.Process.Capabilities.Ambient = userCaps
   155  		}
   156  	}
   157  
   158  	g.SetProcessNoNewPrivileges(s.NoNewPrivileges)
   159  
   160  	if err := setupApparmor(s, rtc, g); err != nil {
   161  		return err
   162  	}
   163  
   164  	// HANDLE SECCOMP
   165  	if s.SeccompProfilePath != "unconfined" {
   166  		seccompConfig, err := getSeccompConfig(s, configSpec, newImage)
   167  		if err != nil {
   168  			return err
   169  		}
   170  		configSpec.Linux.Seccomp = seccompConfig
   171  	}
   172  
   173  	// Clear default Seccomp profile from Generator for unconfined containers
   174  	// and privileged containers which do not specify a seccomp profile.
   175  	if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) {
   176  		configSpec.Linux.Seccomp = nil
   177  	}
   178  
   179  	g.SetRootReadonly(s.ReadOnlyFilesystem)
   180  
   181  	// Add default sysctls
   182  	defaultSysctls, err := util.ValidateSysctls(rtc.Sysctls())
   183  	if err != nil {
   184  		return err
   185  	}
   186  	for sysctlKey, sysctlVal := range defaultSysctls {
   187  
   188  		// Ignore mqueue sysctls if --ipc=host
   189  		if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") {
   190  			logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to host", sysctlKey, sysctlVal)
   191  
   192  			continue
   193  		}
   194  
   195  		// Ignore net sysctls if --net=host
   196  		if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") {
   197  			logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctlKey, sysctlVal)
   198  			continue
   199  		}
   200  
   201  		// Ignore uts sysctls if --uts=host
   202  		if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) {
   203  			logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace set to host", sysctlKey, sysctlVal)
   204  			continue
   205  		}
   206  
   207  		g.AddLinuxSysctl(sysctlKey, sysctlVal)
   208  	}
   209  
   210  	for sysctlKey, sysctlVal := range s.Sysctl {
   211  
   212  		if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") {
   213  			return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since IPC Namespace set to host", sysctlKey, sysctlVal)
   214  		}
   215  
   216  		// Ignore net sysctls if --net=host
   217  		if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") {
   218  			return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since Host Namespace set to host", sysctlKey, sysctlVal)
   219  		}
   220  
   221  		// Ignore uts sysctls if --uts=host
   222  		if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) {
   223  			return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since UTS Namespace set to host", sysctlKey, sysctlVal)
   224  		}
   225  
   226  		g.AddLinuxSysctl(sysctlKey, sysctlVal)
   227  	}
   228  
   229  	return nil
   230  }