...

Source file src/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicyenforcer.go

Documentation: github.com/Microsoft/hcsshim/pkg/securitypolicy

     1  //go:build linux
     2  // +build linux
     3  
     4  package securitypolicy
     5  
     6  import (
     7  	"context"
     8  	"encoding/base64"
     9  	"encoding/json"
    10  	"fmt"
    11  	"regexp"
    12  	"strconv"
    13  	"strings"
    14  	"sync"
    15  	"syscall"
    16  
    17  	oci "github.com/opencontainers/runtime-spec/specs-go"
    18  
    19  	specInternal "github.com/Microsoft/hcsshim/internal/guest/spec"
    20  	"github.com/Microsoft/hcsshim/internal/guestpath"
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  type createEnforcerFunc func(base64EncodedPolicy string, criMounts, criPrivilegedMounts []oci.Mount, maxErrorMessageLength int) (SecurityPolicyEnforcer, error)
    25  
    26  type EnvList []string
    27  
    28  const (
    29  	openDoorEnforcer = "open_door"
    30  	standardEnforcer = "standard"
    31  )
    32  
    33  var (
    34  	registeredEnforcers = map[string]createEnforcerFunc{}
    35  	defaultEnforcer     = standardEnforcer
    36  )
    37  
    38  func init() {
    39  	registeredEnforcers[openDoorEnforcer] = createOpenDoorEnforcer
    40  	registeredEnforcers[standardEnforcer] = createStandardEnforcer
    41  }
    42  
    43  type SecurityPolicyEnforcer interface {
    44  	EnforceDeviceMountPolicy(ctx context.Context, target string, deviceHash string) (err error)
    45  	EnforceDeviceUnmountPolicy(ctx context.Context, unmountTarget string) (err error)
    46  	EnforceOverlayMountPolicy(ctx context.Context, containerID string, layerPaths []string, target string) (err error)
    47  	EnforceOverlayUnmountPolicy(ctx context.Context, target string) (err error)
    48  	EnforceCreateContainerPolicy(
    49  		ctx context.Context,
    50  		sandboxID string,
    51  		containerID string,
    52  		argList []string,
    53  		envList []string,
    54  		workingDir string,
    55  		mounts []oci.Mount,
    56  		privileged bool,
    57  		noNewPrivileges bool,
    58  		user IDName,
    59  		groups []IDName,
    60  		umask string,
    61  		capabilities *oci.LinuxCapabilities,
    62  		seccompProfileSHA256 string,
    63  	) (EnvList, *oci.LinuxCapabilities, bool, error)
    64  	ExtendDefaultMounts([]oci.Mount) error
    65  	EncodedSecurityPolicy() string
    66  	EnforceExecInContainerPolicy(
    67  		ctx context.Context,
    68  		containerID string,
    69  		argList []string,
    70  		envList []string,
    71  		workingDir string,
    72  		noNewPrivileges bool,
    73  		user IDName,
    74  		groups []IDName,
    75  		umask string,
    76  		capabilities *oci.LinuxCapabilities,
    77  	) (EnvList, *oci.LinuxCapabilities, bool, error)
    78  	EnforceExecExternalProcessPolicy(ctx context.Context, argList []string, envList []string, workingDir string) (EnvList, bool, error)
    79  	EnforceShutdownContainerPolicy(ctx context.Context, containerID string) error
    80  	EnforceSignalContainerProcessPolicy(ctx context.Context, containerID string, signal syscall.Signal, isInitProcess bool, startupArgList []string) error
    81  	EnforcePlan9MountPolicy(ctx context.Context, target string) (err error)
    82  	EnforcePlan9UnmountPolicy(ctx context.Context, target string) (err error)
    83  	EnforceGetPropertiesPolicy(ctx context.Context) error
    84  	EnforceDumpStacksPolicy(ctx context.Context) error
    85  	EnforceRuntimeLoggingPolicy(ctx context.Context) (err error)
    86  	LoadFragment(ctx context.Context, issuer string, feed string, code string) error
    87  	EnforceScratchMountPolicy(ctx context.Context, scratchPath string, encrypted bool) (err error)
    88  	EnforceScratchUnmountPolicy(ctx context.Context, scratchPath string) (err error)
    89  	GetUserInfo(containerID string, spec *oci.Process) (IDName, []IDName, string, error)
    90  }
    91  
    92  type stringSet map[string]struct{}
    93  
    94  func (s stringSet) add(item string) {
    95  	s[item] = struct{}{}
    96  }
    97  
    98  func (s stringSet) contains(item string) bool {
    99  	_, contains := s[item]
   100  	return contains
   101  }
   102  
   103  func newSecurityPolicyFromBase64JSON(base64EncodedPolicy string) (*SecurityPolicy, error) {
   104  	// base64 decode the incoming policy string
   105  	// its base64 encoded because it is coming from an annotation
   106  	// annotations are a map of string to string
   107  	// we want to store a complex json object so.... base64 it is
   108  	jsonPolicy, err := base64.StdEncoding.DecodeString(base64EncodedPolicy)
   109  	if err != nil {
   110  		return nil, errors.Wrap(err, "unable to decode policy from Base64 format")
   111  	}
   112  
   113  	// json unmarshall the decoded to a SecurityPolicy
   114  	securityPolicy := new(SecurityPolicy)
   115  	err = json.Unmarshal(jsonPolicy, securityPolicy)
   116  	if err != nil {
   117  		return nil, errors.Wrap(err, "unable to unmarshal JSON policy")
   118  	}
   119  
   120  	return securityPolicy, nil
   121  }
   122  
   123  // createAllowAllEnforcer creates and returns OpenDoorSecurityPolicyEnforcer instance.
   124  // Both AllowAll and Containers cannot be set at the same time.
   125  func createOpenDoorEnforcer(base64EncodedPolicy string, _, _ []oci.Mount, _ int) (SecurityPolicyEnforcer, error) {
   126  	// This covers the case when an "open_door" enforcer was requested, but no
   127  	// actual security policy was passed. This can happen e.g. when a container
   128  	// scratch is created for the first time.
   129  	if base64EncodedPolicy == "" {
   130  		return &OpenDoorSecurityPolicyEnforcer{}, nil
   131  	}
   132  
   133  	securityPolicy, err := newSecurityPolicyFromBase64JSON(base64EncodedPolicy)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	policyContainers := securityPolicy.Containers
   139  	if !securityPolicy.AllowAll || policyContainers.Length > 0 || len(policyContainers.Elements) > 0 {
   140  		return nil, ErrInvalidOpenDoorPolicy
   141  	}
   142  	return &OpenDoorSecurityPolicyEnforcer{
   143  		encodedSecurityPolicy: base64EncodedPolicy,
   144  	}, nil
   145  }
   146  
   147  func (c Containers) toInternal() ([]*securityPolicyContainer, error) {
   148  	containerMapLength := len(c.Elements)
   149  	internal := make([]*securityPolicyContainer, containerMapLength)
   150  
   151  	for i := 0; i < containerMapLength; i++ {
   152  		index := strconv.Itoa(i)
   153  		cConf, ok := c.Elements[index]
   154  		if !ok {
   155  			return nil, fmt.Errorf("container constraint with index %q not found", index)
   156  		}
   157  		cInternal, err := cConf.toInternal()
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  		internal[i] = cInternal
   162  	}
   163  
   164  	return internal, nil
   165  }
   166  
   167  // createStandardEnforcer creates and returns StandardSecurityPolicyEnforcer instance.
   168  // Make sure that the input JSON policy can be converted to internal representation
   169  // and that `criMounts` and `criPrivilegedMounts` can be injected before successful return.
   170  func createStandardEnforcer(
   171  	base64EncodedPolicy string,
   172  	criMounts,
   173  	criPrivilegedMounts []oci.Mount,
   174  	maxErrorMessageLength int,
   175  ) (SecurityPolicyEnforcer, error) {
   176  	securityPolicy, err := newSecurityPolicyFromBase64JSON(base64EncodedPolicy)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	if securityPolicy.AllowAll {
   182  		return createOpenDoorEnforcer(base64EncodedPolicy, criMounts, criPrivilegedMounts, maxErrorMessageLength)
   183  	}
   184  
   185  	containers, err := securityPolicy.Containers.toInternal()
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	enforcer := NewStandardSecurityPolicyEnforcer(containers, base64EncodedPolicy)
   191  
   192  	if err := enforcer.ExtendDefaultMounts(criMounts); err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	addPrivilegedMountsWrapper := WithPrivilegedMounts(criPrivilegedMounts)
   197  	if err := addPrivilegedMountsWrapper(enforcer); err != nil {
   198  		return nil, err
   199  	}
   200  	return enforcer, nil
   201  }
   202  
   203  // CreateSecurityPolicyEnforcer returns an appropriate enforcer for input parameters.
   204  // When `enforcer` isn't return either an AllowAll or default enforcer.
   205  // Returns an error if the requested `enforcer` implementation isn't registered.
   206  func CreateSecurityPolicyEnforcer(
   207  	enforcer string,
   208  	base64EncodedPolicy string,
   209  	criMounts,
   210  	criPrivilegedMounts []oci.Mount,
   211  	maxErrorMessageLength int,
   212  ) (SecurityPolicyEnforcer, error) {
   213  	if enforcer == "" {
   214  		enforcer = defaultEnforcer
   215  		if base64EncodedPolicy == "" {
   216  			enforcer = openDoorEnforcer
   217  		}
   218  	}
   219  	if createEnforcer, ok := registeredEnforcers[enforcer]; !ok {
   220  		return nil, fmt.Errorf("unknown enforcer: %q", enforcer)
   221  	} else {
   222  		return createEnforcer(base64EncodedPolicy, criMounts, criPrivilegedMounts, maxErrorMessageLength)
   223  	}
   224  }
   225  
   226  // newMountConstraint creates an internal mount constraint object from given
   227  // source, destination, type and options.
   228  func newMountConstraint(src, dst string, mType string, mOpts []string) mountInternal {
   229  	return mountInternal{
   230  		Source:      src,
   231  		Destination: dst,
   232  		Type:        mType,
   233  		Options:     mOpts,
   234  	}
   235  }
   236  
   237  type standardEnforcerOpt func(e *StandardSecurityPolicyEnforcer) error
   238  
   239  // WithPrivilegedMounts converts the input mounts to internal mount constraints
   240  // and extends existing internal mount constraints if the container is allowed
   241  // to be executed in elevated mode.
   242  func WithPrivilegedMounts(mounts []oci.Mount) standardEnforcerOpt {
   243  	return func(e *StandardSecurityPolicyEnforcer) error {
   244  		for _, c := range e.Containers {
   245  			if c.AllowElevated {
   246  				for _, m := range mounts {
   247  					mi := mountInternal{
   248  						Source:      m.Source,
   249  						Destination: m.Destination,
   250  						Type:        m.Type,
   251  						Options:     m.Options,
   252  					}
   253  					c.Mounts = append(c.Mounts, mi)
   254  				}
   255  			}
   256  		}
   257  		return nil
   258  	}
   259  }
   260  
   261  // StandardSecurityPolicyEnforcer implements SecurityPolicyEnforcer interface
   262  // and is responsible for enforcing various SecurityPolicy constraints.
   263  //
   264  // Most of the work that this security policy enforcer does it around managing
   265  // state needed to map from a container definition in the SecurityPolicy to
   266  // a specific container ID as we bring up each container. For example, see
   267  // EnforceCreateContainerPolicy where most of the functionality is handling the
   268  // case were policy containers share an overlay and have to try to distinguish
   269  // them based on the command line arguments, environment variables or working
   270  // directory.
   271  //
   272  // Containers that share the same base image, and perhaps further information,
   273  // will have an entry per container instance in the SecurityPolicy. For example,
   274  // a policy that has two containers that use Ubuntu 18.04 will have an entry for
   275  // each even if they share the same command line.
   276  type StandardSecurityPolicyEnforcer struct {
   277  	// encodedSecurityPolicy state is needed for key release
   278  	encodedSecurityPolicy string
   279  	// Containers is the internal representation of users' container policies.
   280  	Containers []*securityPolicyContainer
   281  	// Devices is a mapping between target and a corresponding root hash. Target
   282  	// is a path to a particular block device or its mount point inside UVM and
   283  	// root hash is the dm-verity root hash of that device. Mainly the stored
   284  	// devices represent read-only container layers, but this may change.
   285  	// As the UVM goes through its process of bringing up containers, we have to
   286  	// piece together information about what is going on.
   287  	Devices map[string]string
   288  	// ContainerIndexToContainerIds is a mapping between containers in the
   289  	// SecurityPolicy and possible container IDs that have been created by runc,
   290  	// but have not yet been run.
   291  	//
   292  	// As containers can have exactly the same base image and be "the same" at
   293  	// the time we are doing overlay, the ContainerIndexToContainerIds is a set
   294  	// of possible containers for a given container id. Go doesn't have a set
   295  	// type, so we are doing the idiomatic go thing of using a map[string]struct{}
   296  	// to represent the set.
   297  	ContainerIndexToContainerIds map[int]map[string]struct{}
   298  	// startedContainers is a set of container IDs that were allowed to start.
   299  	// Because Go doesn't have sets as a built-in data structure, we are using a map.
   300  	startedContainers map[string]struct{}
   301  	// mutex guards against concurrent access to fields.
   302  	mutex *sync.Mutex
   303  	// DefaultMounts are mount constraints for container mounts added by CRI and
   304  	// GCS. Since default mounts will be allowed for all containers in the UVM
   305  	// they are not added to each individual policy container and kept as global
   306  	// policy rules.
   307  	DefaultMounts []mountInternal
   308  	// DefaultEnvs are environment variable constraints for variables added
   309  	// by CRI and GCS. Since default envs will be allowed for all containers
   310  	// in the UVM they are not added to each individual policy container and
   311  	// kept as global policy rules.
   312  	DefaultEnvs []EnvRuleConfig
   313  }
   314  
   315  var _ SecurityPolicyEnforcer = (*StandardSecurityPolicyEnforcer)(nil)
   316  
   317  func NewStandardSecurityPolicyEnforcer(
   318  	containers []*securityPolicyContainer,
   319  	encoded string,
   320  ) *StandardSecurityPolicyEnforcer {
   321  	return &StandardSecurityPolicyEnforcer{
   322  		encodedSecurityPolicy:        encoded,
   323  		Containers:                   containers,
   324  		Devices:                      map[string]string{},
   325  		ContainerIndexToContainerIds: map[int]map[string]struct{}{},
   326  		startedContainers:            map[string]struct{}{},
   327  		mutex:                        &sync.Mutex{},
   328  	}
   329  }
   330  
   331  // EnforceDeviceMountPolicy for StandardSecurityPolicyEnforcer validates that
   332  // the target block device's root hash matches any container in SecurityPolicy.
   333  // Block device targets with invalid root hashes are rejected.
   334  //
   335  // At the time that devices are being mounted, we do not know a container
   336  // that they will be used for; only that there is a device with a given root
   337  // hash that being mounted. We check to make sure that the root hash for the
   338  // devices is a root hash that exists for 1 or more layers in any container
   339  // in the supplied SecurityPolicy. Each "seen" layer is recorded in devices
   340  // as it is mounted.
   341  func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceMountPolicy(ctx context.Context, target string, deviceHash string) (err error) {
   342  	pe.mutex.Lock()
   343  	defer pe.mutex.Unlock()
   344  
   345  	if len(pe.Containers) < 1 {
   346  		return errors.New("policy doesn't allow mounting containers")
   347  	}
   348  
   349  	if deviceHash == "" {
   350  		return errors.New("device is missing verity root hash")
   351  	}
   352  
   353  	for _, container := range pe.Containers {
   354  		for _, layer := range container.Layers {
   355  			if deviceHash == layer {
   356  				if existingHash := pe.Devices[target]; existingHash != "" {
   357  					return fmt.Errorf(
   358  						"conflicting device hashes for target %s: old=%s, new=%s",
   359  						target,
   360  						existingHash,
   361  						deviceHash,
   362  					)
   363  				}
   364  				pe.Devices[target] = deviceHash
   365  				return nil
   366  			}
   367  		}
   368  	}
   369  
   370  	return fmt.Errorf("roothash %s for mount %s doesn't match policy", deviceHash, target)
   371  }
   372  
   373  // EnforceDeviceUnmountPolicy for StandardSecurityPolicyEnforcer first validate
   374  // that the target mount was one of the allowed devices and then removes it from
   375  // the mapping.
   376  //
   377  // When proper protocol enforcement is in place, this will also make sure that
   378  // the device isn't currently used by any running container in an overlay.
   379  func (pe *StandardSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(ctx context.Context, unmountTarget string) (err error) {
   380  	pe.mutex.Lock()
   381  	defer pe.mutex.Unlock()
   382  
   383  	if _, ok := pe.Devices[unmountTarget]; !ok {
   384  		return fmt.Errorf("device doesn't exist: %s", unmountTarget)
   385  	}
   386  	delete(pe.Devices, unmountTarget)
   387  
   388  	return nil
   389  }
   390  
   391  // EnforceOverlayMountPolicy for StandardSecurityPolicyEnforcer validates that
   392  // layerPaths represent a valid overlay file system and is allowed by the
   393  // SecurityPolicy.
   394  //
   395  // When overlay filesystems created, look up the root hash chain for an incoming
   396  // overlay and verify it against containers in the policy.
   397  // Overlay filesystem creation is the first time we have a "container ID"
   398  // available to us. The container id identifies the container in question going
   399  // forward. We record the mapping of container index in the policy to a set of
   400  // possible container IDs so that when we have future operations like
   401  // "run command" which come with a container ID, we can find the corresponding
   402  // container index and use that to look up the command in the appropriate
   403  // security policy container instance.
   404  func (pe *StandardSecurityPolicyEnforcer) EnforceOverlayMountPolicy(ctx context.Context, containerID string, layerPaths []string, target string) (err error) {
   405  	pe.mutex.Lock()
   406  	defer pe.mutex.Unlock()
   407  
   408  	if len(pe.Containers) < 1 {
   409  		return errors.New("policy doesn't allow mounting containers")
   410  	}
   411  
   412  	if _, e := pe.startedContainers[containerID]; e {
   413  		return errors.New("container has already been started")
   414  	}
   415  
   416  	var incomingOverlay []string
   417  	for _, layer := range layerPaths {
   418  		if hash, ok := pe.Devices[layer]; !ok {
   419  			return fmt.Errorf("overlay layer isn't mounted: %s", layer)
   420  		} else {
   421  			incomingOverlay = append(incomingOverlay, hash)
   422  		}
   423  	}
   424  
   425  	// check if any of the containers allow the incoming overlay.
   426  	var matchedContainers []int
   427  	for i, container := range pe.Containers {
   428  		if equalForOverlay(incomingOverlay, container.Layers) {
   429  			matchedContainers = append(matchedContainers, i)
   430  		}
   431  	}
   432  
   433  	if len(matchedContainers) == 0 {
   434  		errmsg := fmt.Sprintf("layerPaths '%v' doesn't match any valid overlay", layerPaths)
   435  		return errors.New(errmsg)
   436  	}
   437  
   438  	for _, i := range matchedContainers {
   439  		existing := pe.ContainerIndexToContainerIds[i]
   440  		if len(existing) >= len(matchedContainers) {
   441  			errmsg := fmt.Sprintf("layerPaths '%v' already used in maximum number of container overlays. This is likely because the security policy allows the container to be run only once.", layerPaths)
   442  			return errors.New(errmsg)
   443  		}
   444  		pe.expandMatchesForContainerIndex(i, containerID)
   445  	}
   446  
   447  	return nil
   448  }
   449  
   450  // EnforceCreateContainerPolicy for StandardSecurityPolicyEnforcer validates
   451  // the input container command, env and working directory against containers in
   452  // the SecurityPolicy. The enforcement also narrows down the containers that
   453  // have the same overlays by comparing their command, env and working directory
   454  // rules.
   455  //
   456  // Devices and ContainerIndexToContainerIds are used to build up an
   457  // understanding of the containers running with a UVM as they come up and map
   458  // them back to a container definition from the user supplied SecurityPolicy.
   459  func (pe *StandardSecurityPolicyEnforcer) EnforceCreateContainerPolicy(
   460  	ctx context.Context,
   461  	sandboxID string,
   462  	containerID string,
   463  	argList []string,
   464  	envList []string,
   465  	workingDir string,
   466  	mounts []oci.Mount,
   467  	privileged bool,
   468  	noNewPrivileges bool,
   469  	user IDName,
   470  	groups []IDName,
   471  	umask string,
   472  	caps *oci.LinuxCapabilities,
   473  	seccomp string,
   474  ) (allowedEnvs EnvList,
   475  	allowedCapabilities *oci.LinuxCapabilities,
   476  	stdioAccessAllowed bool,
   477  	err error) {
   478  	pe.mutex.Lock()
   479  	defer pe.mutex.Unlock()
   480  
   481  	if len(pe.Containers) < 1 {
   482  		return nil, nil, true, errors.New("policy doesn't allow mounting containers")
   483  	}
   484  
   485  	if _, e := pe.startedContainers[containerID]; e {
   486  		return nil, nil, true, errors.New("container has already been started")
   487  	}
   488  
   489  	if err = pe.enforceCommandPolicy(containerID, argList); err != nil {
   490  		return nil, nil, true, err
   491  	}
   492  
   493  	if err = pe.enforceEnvironmentVariablePolicy(containerID, envList); err != nil {
   494  		return nil, nil, true, err
   495  	}
   496  
   497  	if err = pe.enforceWorkingDirPolicy(containerID, workingDir); err != nil {
   498  		return nil, nil, true, err
   499  	}
   500  
   501  	if err = pe.enforcePrivilegedPolicy(containerID, privileged); err != nil {
   502  		return nil, nil, true, err
   503  	}
   504  
   505  	if err = pe.enforceMountPolicy(sandboxID, containerID, mounts); err != nil {
   506  		return nil, nil, true, err
   507  	}
   508  
   509  	// record that we've allowed this container to start
   510  	pe.startedContainers[containerID] = struct{}{}
   511  
   512  	return envList, caps, true, nil
   513  }
   514  
   515  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   516  // points are simply allowed.
   517  func (*StandardSecurityPolicyEnforcer) EnforceExecInContainerPolicy(_ context.Context, _ string, _ []string, envList []string, _ string, _ bool, _ IDName, _ []IDName, _ string, caps *oci.LinuxCapabilities) (EnvList, *oci.LinuxCapabilities, bool, error) {
   518  	return envList, caps, true, nil
   519  }
   520  
   521  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   522  // points are simply allowed.
   523  func (*StandardSecurityPolicyEnforcer) EnforceExecExternalProcessPolicy(_ context.Context, _ []string, envList []string, _ string) (EnvList, bool, error) {
   524  	return envList, true, nil
   525  }
   526  
   527  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   528  // points are simply allowed.
   529  func (*StandardSecurityPolicyEnforcer) EnforceShutdownContainerPolicy(context.Context, string) error {
   530  	return nil
   531  }
   532  
   533  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   534  // points are simply allowed.
   535  func (*StandardSecurityPolicyEnforcer) EnforceSignalContainerProcessPolicy(context.Context, string, syscall.Signal, bool, []string) error {
   536  	return nil
   537  }
   538  
   539  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   540  // points are simply allowed.
   541  func (*StandardSecurityPolicyEnforcer) EnforcePlan9MountPolicy(context.Context, string) error {
   542  	return nil
   543  }
   544  
   545  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   546  // points are simply allowed.
   547  func (*StandardSecurityPolicyEnforcer) EnforcePlan9UnmountPolicy(context.Context, string) error {
   548  	return nil
   549  }
   550  
   551  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   552  // points are simply allowed.
   553  func (*StandardSecurityPolicyEnforcer) EnforceOverlayUnmountPolicy(context.Context, string) error {
   554  	return nil
   555  }
   556  
   557  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   558  // points are simply allowed.
   559  func (*StandardSecurityPolicyEnforcer) EnforceGetPropertiesPolicy(context.Context) error {
   560  	return nil
   561  }
   562  
   563  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   564  // points are simply allowed.
   565  func (*StandardSecurityPolicyEnforcer) EnforceDumpStacksPolicy(context.Context) error {
   566  	return nil
   567  }
   568  
   569  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   570  // points are simply allowed.
   571  func (*StandardSecurityPolicyEnforcer) EnforceRuntimeLoggingPolicy(context.Context) error {
   572  	return nil
   573  }
   574  
   575  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   576  // points are simply allowed.
   577  func (*StandardSecurityPolicyEnforcer) LoadFragment(context.Context, string, string, string) error {
   578  	return nil
   579  }
   580  
   581  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   582  // points are simply allowed.
   583  func (StandardSecurityPolicyEnforcer) EnforceScratchMountPolicy(context.Context, string, bool) error {
   584  	return nil
   585  }
   586  
   587  // Stub. We are deprecating the standard enforcer. Newly added enforcement
   588  // points are simply allowed.
   589  func (StandardSecurityPolicyEnforcer) EnforceScratchUnmountPolicy(context.Context, string) error {
   590  	return nil
   591  }
   592  
   593  // Stub. We are deprecating the standard enforcer.
   594  func (StandardSecurityPolicyEnforcer) GetUserInfo(containerID string, spec *oci.Process) (IDName, []IDName, string, error) {
   595  	return IDName{}, nil, "", nil
   596  }
   597  
   598  func (pe *StandardSecurityPolicyEnforcer) enforceCommandPolicy(containerID string, argList []string) (err error) {
   599  	// Get a list of all the indexes into our security policy's list of
   600  	// containers that are possible matches for this containerID based
   601  	// on the image overlay layout
   602  	possibleIndices := pe.possibleIndicesForID(containerID)
   603  
   604  	// Loop through every possible match and do two things:
   605  	// 1- see if any command matches. we need at least one match or
   606  	//    we don't allow the container to start
   607  	// 2- remove this containerID as a possible match for any container from the
   608  	//    security policy whose command line isn't a match.
   609  	matchingCommandFound := false
   610  	for _, possibleIndex := range possibleIndices {
   611  		cmd := pe.Containers[possibleIndex].Command
   612  		if stringSlicesEqual(cmd, argList) {
   613  			matchingCommandFound = true
   614  		} else {
   615  			// a possible matching index turned out not to match, so we
   616  			// need to update that list and remove it
   617  			pe.narrowMatchesForContainerIndex(possibleIndex, containerID)
   618  		}
   619  	}
   620  
   621  	if !matchingCommandFound {
   622  		errmsg := fmt.Sprintf("command %v doesn't match policy", argList)
   623  		return errors.New(errmsg)
   624  	}
   625  
   626  	return nil
   627  }
   628  
   629  func (pe *StandardSecurityPolicyEnforcer) enforceEnvironmentVariablePolicy(containerID string, envList []string) (err error) {
   630  	// Get a list of all the indexes into our security policy's list of
   631  	// containers that are possible matches for this containerID based
   632  	// on the image overlay layout and command line
   633  	possibleIndices := pe.possibleIndicesForID(containerID)
   634  
   635  	for _, envVariable := range envList {
   636  		matchingRuleFoundForSomeContainer := false
   637  		for _, possibleIndex := range possibleIndices {
   638  			envRules := pe.Containers[possibleIndex].EnvRules
   639  			ok := envIsMatchedByRule(envVariable, envRules)
   640  			if ok {
   641  				matchingRuleFoundForSomeContainer = true
   642  			} else {
   643  				// a possible matching index turned out not to match, so we
   644  				// need to update that list and remove it
   645  				pe.narrowMatchesForContainerIndex(possibleIndex, containerID)
   646  			}
   647  		}
   648  
   649  		if !matchingRuleFoundForSomeContainer {
   650  			return fmt.Errorf("env variable %s unmatched by policy rule", envVariable)
   651  		}
   652  	}
   653  
   654  	return nil
   655  }
   656  
   657  func (pe *StandardSecurityPolicyEnforcer) enforceWorkingDirPolicy(containerID string, workingDir string) error {
   658  	possibleIndices := pe.possibleIndicesForID(containerID)
   659  
   660  	matched := false
   661  	for _, pIndex := range possibleIndices {
   662  		pWorkingDir := pe.Containers[pIndex].WorkingDir
   663  		if pWorkingDir == workingDir {
   664  			matched = true
   665  		} else {
   666  			pe.narrowMatchesForContainerIndex(pIndex, containerID)
   667  		}
   668  	}
   669  	if !matched {
   670  		return fmt.Errorf("working_dir %q unmatched by policy rule", workingDir)
   671  	}
   672  	return nil
   673  }
   674  
   675  func (pe *StandardSecurityPolicyEnforcer) enforcePrivilegedPolicy(containerID string, privileged bool) error {
   676  	// We only need to check for privilege escalation
   677  	if !privileged {
   678  		return nil
   679  	}
   680  
   681  	possibleIndices := pe.possibleIndicesForID(containerID)
   682  
   683  	matched := false
   684  	for _, pIndex := range possibleIndices {
   685  		pAllowElevated := pe.Containers[pIndex].AllowElevated
   686  		if pAllowElevated {
   687  			matched = true
   688  		} else {
   689  			pe.narrowMatchesForContainerIndex(pIndex, containerID)
   690  		}
   691  	}
   692  	if !matched {
   693  		return errors.New("privileged escalation unmatched by policy rule")
   694  	}
   695  	return nil
   696  }
   697  
   698  func envIsMatchedByRule(envVariable string, rules []EnvRuleConfig) bool {
   699  	for _, rule := range rules {
   700  		switch rule.Strategy {
   701  		case "string":
   702  			if rule.Rule == envVariable {
   703  				return true
   704  			}
   705  		case "re2":
   706  			// if the match errors out, we don't care. it's not a match
   707  			matched, _ := regexp.MatchString(rule.Rule, envVariable)
   708  			if matched {
   709  				return true
   710  			}
   711  		}
   712  	}
   713  
   714  	return false
   715  }
   716  
   717  // StandardSecurityPolicyEnforcer.mutex lock must be held prior to calling this function.
   718  func (pe *StandardSecurityPolicyEnforcer) expandMatchesForContainerIndex(index int, idToAdd string) {
   719  	_, keyExists := pe.ContainerIndexToContainerIds[index]
   720  	if !keyExists {
   721  		pe.ContainerIndexToContainerIds[index] = map[string]struct{}{}
   722  	}
   723  
   724  	pe.ContainerIndexToContainerIds[index][idToAdd] = struct{}{}
   725  }
   726  
   727  // StandardSecurityPolicyEnforcer.mutex lock must be held prior to calling this function.
   728  func (pe *StandardSecurityPolicyEnforcer) narrowMatchesForContainerIndex(index int, idToRemove string) {
   729  	delete(pe.ContainerIndexToContainerIds[index], idToRemove)
   730  }
   731  
   732  func equalForOverlay(a1 []string, a2 []string) bool {
   733  	// We've stored the layers from bottom to top they are in layerPaths as
   734  	// top to bottom (the order a string gets concatenated for the unix mount
   735  	// command). W do our check with that in mind.
   736  	if len(a1) != len(a2) {
   737  		return false
   738  	}
   739  	topIndex := len(a2) - 1
   740  	for i, v := range a1 {
   741  		if v != a2[topIndex-i] {
   742  			return false
   743  		}
   744  	}
   745  	return true
   746  }
   747  
   748  // StandardSecurityPolicyEnforcer.mutex lock must be held prior to calling this function.
   749  func (pe *StandardSecurityPolicyEnforcer) possibleIndicesForID(containerID string) []int {
   750  	var possibleIndices []int
   751  	for index, ids := range pe.ContainerIndexToContainerIds {
   752  		for id := range ids {
   753  			if containerID == id {
   754  				possibleIndices = append(possibleIndices, index)
   755  			}
   756  		}
   757  	}
   758  	return possibleIndices
   759  }
   760  
   761  func (pe *StandardSecurityPolicyEnforcer) enforceDefaultMounts(specMount oci.Mount) error {
   762  	for _, mountConstraint := range pe.DefaultMounts {
   763  		if err := mountConstraint.validate(specMount); err == nil {
   764  			return nil
   765  		}
   766  	}
   767  	return fmt.Errorf("mount not allowed by default mount constraints: %+v", specMount)
   768  }
   769  
   770  // ExtendDefaultMounts for StandardSecurityPolicyEnforcer adds default mounts
   771  // added by CRI and GCS to the list of DefaultMounts, which are always allowed.
   772  func (pe *StandardSecurityPolicyEnforcer) ExtendDefaultMounts(defaultMounts []oci.Mount) error {
   773  	pe.mutex.Lock()
   774  	defer pe.mutex.Unlock()
   775  
   776  	for _, mnt := range defaultMounts {
   777  		pe.DefaultMounts = append(pe.DefaultMounts, newMountConstraint(
   778  			mnt.Source,
   779  			mnt.Destination,
   780  			mnt.Type,
   781  			mnt.Options,
   782  		))
   783  	}
   784  	return nil
   785  }
   786  
   787  // enforceMountPolicy for StandardSecurityPolicyEnforcer validates various
   788  // default mounts injected into container spec by GCS or containerD. As part of
   789  // the enforcement, the method also narrows down possible container IDs with
   790  // the same overlay.
   791  func (pe *StandardSecurityPolicyEnforcer) enforceMountPolicy(sandboxID, containerID string, mounts []oci.Mount) (err error) {
   792  	possibleIndices := pe.possibleIndicesForID(containerID)
   793  
   794  	for _, mount := range mounts {
   795  		// first check against default mounts
   796  		if err := pe.enforceDefaultMounts(mount); err == nil {
   797  			continue
   798  		}
   799  
   800  		mountOk := false
   801  		// check against user provided mount constraints, which helps to figure
   802  		// out which container this mount spec corresponds to.
   803  		for _, pIndex := range possibleIndices {
   804  			cont := pe.Containers[pIndex]
   805  			if err = cont.matchMount(sandboxID, mount); err == nil {
   806  				mountOk = true
   807  			} else {
   808  				pe.narrowMatchesForContainerIndex(pIndex, containerID)
   809  			}
   810  		}
   811  
   812  		if !mountOk {
   813  			retErr := fmt.Errorf("mount %+v is not allowed by mount constraints", mount)
   814  			return retErr
   815  		}
   816  	}
   817  
   818  	return nil
   819  }
   820  
   821  // validate checks given OCI mount against mount policy. Destination is checked
   822  // by direct string comparisons and Source is checked via a regular expression.
   823  // This is done this way, because container path (Destination) is always fixed,
   824  // however, the host/UVM path (Source) can include IDs generated at runtime and
   825  // impossible to know in advance.
   826  //
   827  // NOTE: Different matching strategies can be added by introducing a separate
   828  // path matching config, which isn't needed at the moment.
   829  func (m *mountInternal) validate(mSpec oci.Mount) error {
   830  	if m.Type != mSpec.Type {
   831  		return fmt.Errorf("mount type not allowed by policy: expected=%q, actual=%q", m.Type, mSpec.Type)
   832  	}
   833  	if ok, _ := regexp.MatchString(m.Source, mSpec.Source); !ok {
   834  		return fmt.Errorf("mount source not allowed by policy: expected=%q, actual=%q", m.Source, mSpec.Source)
   835  	}
   836  	if m.Destination != mSpec.Destination && m.Destination != "" {
   837  		return fmt.Errorf("mount destination not allowed by policy: expected=%q, actual=%q", m.Destination, mSpec.Destination)
   838  	}
   839  	if !stringSlicesEqual(m.Options, mSpec.Options) {
   840  		return fmt.Errorf("mount options not allowed by policy: expected=%q, actual=%q", m.Options, mSpec.Options)
   841  	}
   842  	return nil
   843  }
   844  
   845  // matchMount matches given OCI mount against mount constraints. If no match
   846  // found, the mount is not allowed.
   847  func (c *securityPolicyContainer) matchMount(sandboxID string, m oci.Mount) (err error) {
   848  	for _, constraint := range c.Mounts {
   849  		// now that we know the sandboxID we can get the actual path for
   850  		// various destination path types by adding a UVM mount prefix
   851  		constraint = substituteUVMPath(sandboxID, constraint)
   852  		if err = constraint.validate(m); err == nil {
   853  			return nil
   854  		}
   855  	}
   856  	return fmt.Errorf("mount is not allowed by policy: %+v", m)
   857  }
   858  
   859  // substituteUVMPath substitutes mount prefix to an appropriate path inside
   860  // UVM. At policy generation time, it's impossible to tell what the sandboxID
   861  // will be, so the prefix substitution needs to happen during runtime.
   862  func substituteUVMPath(sandboxID string, m mountInternal) mountInternal {
   863  	if strings.HasPrefix(m.Source, guestpath.SandboxMountPrefix) {
   864  		m.Source = specInternal.SandboxMountSource(sandboxID, m.Source)
   865  	} else if strings.HasPrefix(m.Source, guestpath.HugePagesMountPrefix) {
   866  		m.Source = specInternal.HugePagesMountSource(sandboxID, m.Source)
   867  	}
   868  	return m
   869  }
   870  
   871  func stringSlicesEqual(slice1, slice2 []string) bool {
   872  	if len(slice1) != len(slice2) {
   873  		return false
   874  	}
   875  
   876  	for i := 0; i < len(slice1); i++ {
   877  		if slice1[i] != slice2[i] {
   878  			return false
   879  		}
   880  	}
   881  	return true
   882  }
   883  
   884  func (pe *StandardSecurityPolicyEnforcer) EncodedSecurityPolicy() string {
   885  	return pe.encodedSecurityPolicy
   886  }
   887  
   888  type OpenDoorSecurityPolicyEnforcer struct {
   889  	encodedSecurityPolicy string
   890  }
   891  
   892  var _ SecurityPolicyEnforcer = (*OpenDoorSecurityPolicyEnforcer)(nil)
   893  
   894  func (OpenDoorSecurityPolicyEnforcer) EnforceDeviceMountPolicy(context.Context, string, string) error {
   895  	return nil
   896  }
   897  
   898  func (OpenDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(context.Context, string) error {
   899  	return nil
   900  }
   901  
   902  func (OpenDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(context.Context, string, []string, string) error {
   903  	return nil
   904  }
   905  
   906  func (OpenDoorSecurityPolicyEnforcer) EnforceOverlayUnmountPolicy(context.Context, string) error {
   907  	return nil
   908  }
   909  
   910  func (OpenDoorSecurityPolicyEnforcer) EnforceCreateContainerPolicy(_ context.Context, _, _ string, _ []string, envList []string, _ string, _ []oci.Mount, _ bool, _ bool, _ IDName, _ []IDName, _ string, caps *oci.LinuxCapabilities, _ string) (EnvList, *oci.LinuxCapabilities, bool, error) {
   911  	return envList, caps, true, nil
   912  }
   913  
   914  func (OpenDoorSecurityPolicyEnforcer) EnforceExecInContainerPolicy(_ context.Context, _ string, _ []string, envList []string, _ string, _ bool, _ IDName, _ []IDName, _ string, caps *oci.LinuxCapabilities) (EnvList, *oci.LinuxCapabilities, bool, error) {
   915  	return envList, caps, true, nil
   916  }
   917  
   918  func (OpenDoorSecurityPolicyEnforcer) EnforceExecExternalProcessPolicy(_ context.Context, _ []string, envList []string, _ string) (EnvList, bool, error) {
   919  	return envList, true, nil
   920  }
   921  
   922  func (*OpenDoorSecurityPolicyEnforcer) EnforceShutdownContainerPolicy(context.Context, string) error {
   923  	return nil
   924  }
   925  
   926  func (*OpenDoorSecurityPolicyEnforcer) EnforceSignalContainerProcessPolicy(context.Context, string, syscall.Signal, bool, []string) error {
   927  	return nil
   928  }
   929  
   930  func (*OpenDoorSecurityPolicyEnforcer) EnforcePlan9MountPolicy(context.Context, string) error {
   931  	return nil
   932  }
   933  
   934  func (*OpenDoorSecurityPolicyEnforcer) EnforcePlan9UnmountPolicy(context.Context, string) error {
   935  	return nil
   936  }
   937  
   938  func (OpenDoorSecurityPolicyEnforcer) EnforceGetPropertiesPolicy(context.Context) error {
   939  	return nil
   940  }
   941  
   942  func (OpenDoorSecurityPolicyEnforcer) EnforceDumpStacksPolicy(context.Context) error {
   943  	return nil
   944  }
   945  
   946  func (OpenDoorSecurityPolicyEnforcer) LoadFragment(context.Context, string, string, string) error {
   947  	return nil
   948  }
   949  
   950  func (OpenDoorSecurityPolicyEnforcer) ExtendDefaultMounts([]oci.Mount) error {
   951  	return nil
   952  }
   953  
   954  func (OpenDoorSecurityPolicyEnforcer) EnforceRuntimeLoggingPolicy(context.Context) error {
   955  	return nil
   956  }
   957  
   958  func (oe *OpenDoorSecurityPolicyEnforcer) EncodedSecurityPolicy() string {
   959  	return oe.encodedSecurityPolicy
   960  }
   961  
   962  func (OpenDoorSecurityPolicyEnforcer) EnforceScratchMountPolicy(context.Context, string, bool) error {
   963  	return nil
   964  }
   965  
   966  func (OpenDoorSecurityPolicyEnforcer) EnforceScratchUnmountPolicy(context.Context, string) error {
   967  	return nil
   968  }
   969  
   970  func (OpenDoorSecurityPolicyEnforcer) GetUserInfo(containerID string, spec *oci.Process) (IDName, []IDName, string, error) {
   971  	return IDName{}, nil, "", nil
   972  }
   973  
   974  type ClosedDoorSecurityPolicyEnforcer struct {
   975  	encodedSecurityPolicy string //nolint:unused
   976  }
   977  
   978  var _ SecurityPolicyEnforcer = (*ClosedDoorSecurityPolicyEnforcer)(nil)
   979  
   980  func (ClosedDoorSecurityPolicyEnforcer) EnforceDeviceMountPolicy(context.Context, string, string) error {
   981  	return errors.New("mounting is denied by policy")
   982  }
   983  
   984  func (ClosedDoorSecurityPolicyEnforcer) EnforceDeviceUnmountPolicy(context.Context, string) error {
   985  	return errors.New("unmounting is denied by policy")
   986  }
   987  
   988  func (ClosedDoorSecurityPolicyEnforcer) EnforceOverlayMountPolicy(context.Context, string, []string, string) error {
   989  	return errors.New("creating an overlay fs is denied by policy")
   990  }
   991  
   992  func (ClosedDoorSecurityPolicyEnforcer) EnforceOverlayUnmountPolicy(context.Context, string) error {
   993  	return errors.New("removing an overlay fs is denied by policy")
   994  }
   995  
   996  func (ClosedDoorSecurityPolicyEnforcer) EnforceCreateContainerPolicy(context.Context, string, string, []string, []string, string, []oci.Mount, bool, bool, IDName, []IDName, string, *oci.LinuxCapabilities, string) (EnvList, *oci.LinuxCapabilities, bool, error) {
   997  	return nil, nil, false, errors.New("running commands is denied by policy")
   998  }
   999  
  1000  func (ClosedDoorSecurityPolicyEnforcer) EnforceExecInContainerPolicy(context.Context, string, []string, []string, string, bool, IDName, []IDName, string, *oci.LinuxCapabilities) (EnvList, *oci.LinuxCapabilities, bool, error) {
  1001  	return nil, nil, false, errors.New("starting additional processes in a container is denied by policy")
  1002  }
  1003  
  1004  func (ClosedDoorSecurityPolicyEnforcer) EnforceExecExternalProcessPolicy(context.Context, []string, []string, string) (EnvList, bool, error) {
  1005  	return nil, false, errors.New("starting additional processes in uvm is denied by policy")
  1006  }
  1007  
  1008  func (*ClosedDoorSecurityPolicyEnforcer) EnforceShutdownContainerPolicy(context.Context, string) error {
  1009  	return errors.New("shutting down containers is denied by policy")
  1010  }
  1011  
  1012  func (*ClosedDoorSecurityPolicyEnforcer) EnforceSignalContainerProcessPolicy(context.Context, string, syscall.Signal, bool, []string) error {
  1013  	return errors.New("signalling container processes is denied by policy")
  1014  }
  1015  
  1016  func (*ClosedDoorSecurityPolicyEnforcer) EnforcePlan9MountPolicy(context.Context, string) error {
  1017  	return errors.New("mounting is denied by policy")
  1018  }
  1019  
  1020  func (*ClosedDoorSecurityPolicyEnforcer) EnforcePlan9UnmountPolicy(context.Context, string) error {
  1021  	return errors.New("unmounting is denied by policy")
  1022  }
  1023  
  1024  func (ClosedDoorSecurityPolicyEnforcer) EnforceGetPropertiesPolicy(context.Context) error {
  1025  	return errors.New("getting container properties is denied by policy")
  1026  }
  1027  
  1028  func (ClosedDoorSecurityPolicyEnforcer) EnforceDumpStacksPolicy(context.Context) error {
  1029  	return errors.New("getting stack dumps is denied by policy")
  1030  }
  1031  
  1032  func (ClosedDoorSecurityPolicyEnforcer) LoadFragment(context.Context, string, string, string) error {
  1033  	return errors.New("loading fragments is denied by policy")
  1034  }
  1035  
  1036  func (ClosedDoorSecurityPolicyEnforcer) ExtendDefaultMounts(_ []oci.Mount) error {
  1037  	return nil
  1038  }
  1039  
  1040  func (ClosedDoorSecurityPolicyEnforcer) EnforceRuntimeLoggingPolicy(context.Context) error {
  1041  	return errors.New("runtime logging is denied by policy")
  1042  }
  1043  
  1044  func (ClosedDoorSecurityPolicyEnforcer) EncodedSecurityPolicy() string {
  1045  	return ""
  1046  }
  1047  
  1048  func (ClosedDoorSecurityPolicyEnforcer) EnforceScratchMountPolicy(context.Context, string, bool) error {
  1049  	return errors.New("mounting scratch is denied by the policy")
  1050  }
  1051  
  1052  func (ClosedDoorSecurityPolicyEnforcer) EnforceScratchUnmountPolicy(context.Context, string) error {
  1053  	return errors.New("unmounting scratch is denied by the policy")
  1054  }
  1055  
  1056  func (ClosedDoorSecurityPolicyEnforcer) GetUserInfo(containerID string, spec *oci.Process) (IDName, []IDName, string, error) {
  1057  	return IDName{}, nil, "", nil
  1058  }
  1059  

View as plain text