github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/spec/security.go (about)

     1  package createconfig
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/containers/common/pkg/capabilities"
     8  	"github.com/containers/libpod/libpod"
     9  	"github.com/containers/libpod/pkg/util"
    10  	"github.com/opencontainers/runtime-tools/generate"
    11  	"github.com/opencontainers/selinux/go-selinux/label"
    12  	"github.com/pkg/errors"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  // ToCreateOptions convert the SecurityConfig to a slice of container create
    17  // options.
    18  func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) {
    19  	options := make([]libpod.CtrCreateOption, 0)
    20  	options = append(options, libpod.WithSecLabels(c.LabelOpts))
    21  	options = append(options, libpod.WithPrivileged(c.Privileged))
    22  	return options, nil
    23  }
    24  
    25  // SetLabelOpts sets the label options of the SecurityConfig according to the
    26  // input.
    27  func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error {
    28  	if c.Privileged {
    29  		c.LabelOpts = label.DisableSecOpt()
    30  		return nil
    31  	}
    32  
    33  	var labelOpts []string
    34  	if pidConfig.PidMode.IsHost() {
    35  		labelOpts = append(labelOpts, label.DisableSecOpt()...)
    36  	} else if pidConfig.PidMode.IsContainer() {
    37  		ctr, err := runtime.LookupContainer(pidConfig.PidMode.Container())
    38  		if err != nil {
    39  			return errors.Wrapf(err, "container %q not found", pidConfig.PidMode.Container())
    40  		}
    41  		secopts, err := label.DupSecOpt(ctr.ProcessLabel())
    42  		if err != nil {
    43  			return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
    44  		}
    45  		labelOpts = append(labelOpts, secopts...)
    46  	}
    47  
    48  	if ipcConfig.IpcMode.IsHost() {
    49  		labelOpts = append(labelOpts, label.DisableSecOpt()...)
    50  	} else if ipcConfig.IpcMode.IsContainer() {
    51  		ctr, err := runtime.LookupContainer(ipcConfig.IpcMode.Container())
    52  		if err != nil {
    53  			return errors.Wrapf(err, "container %q not found", ipcConfig.IpcMode.Container())
    54  		}
    55  		secopts, err := label.DupSecOpt(ctr.ProcessLabel())
    56  		if err != nil {
    57  			return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
    58  		}
    59  		labelOpts = append(labelOpts, secopts...)
    60  	}
    61  
    62  	c.LabelOpts = append(c.LabelOpts, labelOpts...)
    63  	return nil
    64  }
    65  
    66  // SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.).
    67  func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error {
    68  	for _, opt := range securityOpts {
    69  		if opt == "no-new-privileges" {
    70  			c.NoNewPrivs = true
    71  		} else {
    72  			con := strings.SplitN(opt, "=", 2)
    73  			if len(con) != 2 {
    74  				return fmt.Errorf("invalid --security-opt 1: %q", opt)
    75  			}
    76  
    77  			switch con[0] {
    78  			case "label":
    79  				c.LabelOpts = append(c.LabelOpts, con[1])
    80  			case "apparmor":
    81  				c.ApparmorProfile = con[1]
    82  			case "seccomp":
    83  				c.SeccompProfilePath = con[1]
    84  			default:
    85  				return fmt.Errorf("invalid --security-opt 2: %q", opt)
    86  			}
    87  		}
    88  	}
    89  
    90  	if c.SeccompProfilePath == "" {
    91  		var err error
    92  		c.SeccompProfilePath, err = libpod.DefaultSeccompPath()
    93  		if err != nil {
    94  			return err
    95  		}
    96  	}
    97  	c.SecurityOpts = securityOpts
    98  	return nil
    99  }
   100  
   101  // ConfigureGenerator configures the generator according to the input.
   102  func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error {
   103  	// HANDLE CAPABILITIES
   104  	// NOTE: Must happen before SECCOMP
   105  	if c.Privileged {
   106  		g.SetupPrivileged(true)
   107  	}
   108  
   109  	useNotRoot := func(user string) bool {
   110  		if user == "" || user == "root" || user == "0" {
   111  			return false
   112  		}
   113  		return true
   114  	}
   115  
   116  	configSpec := g.Config
   117  	var err error
   118  	var defaultCaplist []string
   119  	bounding := configSpec.Process.Capabilities.Bounding
   120  	if useNotRoot(user.User) {
   121  		configSpec.Process.Capabilities.Bounding = defaultCaplist
   122  	}
   123  	defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	privCapRequired := []string{}
   129  
   130  	if !c.Privileged && len(c.CapRequired) > 0 {
   131  		// Pass CapRequired in CapAdd field to normalize capabilities names
   132  		capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil)
   133  		if err != nil {
   134  			logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ","))
   135  		} else {
   136  			// Verify all capRequiered are in the defaultCapList
   137  			for _, cap := range capRequired {
   138  				if !util.StringInSlice(cap, defaultCaplist) {
   139  					privCapRequired = append(privCapRequired, cap)
   140  				}
   141  			}
   142  		}
   143  		if len(privCapRequired) == 0 {
   144  			defaultCaplist = capRequired
   145  		} else {
   146  			logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ","))
   147  		}
   148  	}
   149  	configSpec.Process.Capabilities.Bounding = defaultCaplist
   150  	configSpec.Process.Capabilities.Permitted = defaultCaplist
   151  	configSpec.Process.Capabilities.Inheritable = defaultCaplist
   152  	configSpec.Process.Capabilities.Effective = defaultCaplist
   153  	configSpec.Process.Capabilities.Ambient = defaultCaplist
   154  	if useNotRoot(user.User) {
   155  		defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop)
   156  		if err != nil {
   157  			return err
   158  		}
   159  	}
   160  	configSpec.Process.Capabilities.Bounding = defaultCaplist
   161  
   162  	// HANDLE SECCOMP
   163  	if c.SeccompProfilePath != "unconfined" {
   164  		seccompConfig, err := getSeccompConfig(c, configSpec)
   165  		if err != nil {
   166  			return err
   167  		}
   168  		configSpec.Linux.Seccomp = seccompConfig
   169  	}
   170  
   171  	// Clear default Seccomp profile from Generator for privileged containers
   172  	if c.SeccompProfilePath == "unconfined" || c.Privileged {
   173  		configSpec.Linux.Seccomp = nil
   174  	}
   175  
   176  	for _, opt := range c.SecurityOpts {
   177  		// Split on both : and =
   178  		splitOpt := strings.Split(opt, "=")
   179  		if len(splitOpt) == 1 {
   180  			splitOpt = strings.Split(opt, ":")
   181  		}
   182  		if len(splitOpt) < 2 {
   183  			continue
   184  		}
   185  		switch splitOpt[0] {
   186  		case "label":
   187  			configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
   188  		case "seccomp":
   189  			configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
   190  		case "apparmor":
   191  			configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1]
   192  		}
   193  	}
   194  
   195  	g.SetRootReadonly(c.ReadOnlyRootfs)
   196  	for sysctlKey, sysctlVal := range c.Sysctl {
   197  		g.AddLinuxSysctl(sysctlKey, sysctlVal)
   198  	}
   199  
   200  	return nil
   201  }