github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/specgen/generate/security.go (about)

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