github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/container/run_security_linux.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package container
    18  
    19  import (
    20  	"errors"
    21  	"strings"
    22  	"sync"
    23  
    24  	"github.com/containerd/containerd/contrib/apparmor"
    25  	"github.com/containerd/containerd/contrib/seccomp"
    26  	"github.com/containerd/containerd/oci"
    27  	"github.com/containerd/containerd/pkg/cap"
    28  	"github.com/containerd/log"
    29  	"github.com/containerd/nerdctl/v2/pkg/apparmorutil"
    30  	"github.com/containerd/nerdctl/v2/pkg/defaults"
    31  	"github.com/containerd/nerdctl/v2/pkg/maputil"
    32  	"github.com/containerd/nerdctl/v2/pkg/strutil"
    33  )
    34  
    35  var privilegedOpts = []oci.SpecOpts{
    36  	oci.WithPrivileged,
    37  	oci.WithAllDevicesAllowed,
    38  	oci.WithHostDevices,
    39  	oci.WithNewPrivileges,
    40  }
    41  
    42  var privilegedWithoutDevicesOpts = []oci.SpecOpts{
    43  	oci.WithPrivileged,
    44  	oci.WithNewPrivileges,
    45  }
    46  
    47  func generateSecurityOpts(privileged bool, securityOptsMap map[string]string) ([]oci.SpecOpts, error) {
    48  	for k := range securityOptsMap {
    49  		switch k {
    50  		case "seccomp", "apparmor", "no-new-privileges", "privileged-without-host-devices":
    51  		default:
    52  			log.L.Warnf("unknown security-opt: %q", k)
    53  		}
    54  	}
    55  	var opts []oci.SpecOpts
    56  	if seccompProfile, ok := securityOptsMap["seccomp"]; ok && seccompProfile != defaults.SeccompProfileName {
    57  		if seccompProfile == "" {
    58  			return nil, errors.New("invalid security-opt \"seccomp\"")
    59  		}
    60  
    61  		if seccompProfile != "unconfined" {
    62  			opts = append(opts, seccomp.WithProfile(seccompProfile))
    63  		}
    64  	} else {
    65  		opts = append(opts, seccomp.WithDefaultProfile())
    66  	}
    67  
    68  	canLoadNewAppArmor := apparmorutil.CanLoadNewProfile()
    69  	canApplyExistingProfile := apparmorutil.CanApplyExistingProfile()
    70  	if aaProfile, ok := securityOptsMap["apparmor"]; ok {
    71  		if aaProfile == "" {
    72  			return nil, errors.New("invalid security-opt \"apparmor\"")
    73  		}
    74  		if aaProfile != "unconfined" {
    75  			if !canApplyExistingProfile {
    76  				log.L.Warnf("the host does not support AppArmor. Ignoring profile %q", aaProfile)
    77  			} else {
    78  				opts = append(opts, apparmor.WithProfile(aaProfile))
    79  			}
    80  		}
    81  	} else {
    82  		if canLoadNewAppArmor {
    83  			if err := apparmor.LoadDefaultProfile(defaults.AppArmorProfileName); err != nil {
    84  				return nil, err
    85  			}
    86  		}
    87  		if apparmorutil.CanApplySpecificExistingProfile(defaults.AppArmorProfileName) {
    88  			opts = append(opts, apparmor.WithProfile(defaults.AppArmorProfileName))
    89  		}
    90  	}
    91  
    92  	nnp, err := maputil.MapBoolValueAsOpt(securityOptsMap, "no-new-privileges")
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	if !nnp {
    98  		opts = append(opts, oci.WithNewPrivileges)
    99  	}
   100  
   101  	privilegedWithoutHostDevices, err := maputil.MapBoolValueAsOpt(securityOptsMap, "privileged-without-host-devices")
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	if privilegedWithoutHostDevices && !privileged {
   107  		return nil, errors.New("flag `--security-opt privileged-without-host-devices` can't be used without `--privileged` enabled")
   108  	}
   109  
   110  	if privileged {
   111  		if privilegedWithoutHostDevices {
   112  			opts = append(opts, privilegedWithoutDevicesOpts...)
   113  		} else {
   114  			opts = append(opts, privilegedOpts...)
   115  		}
   116  	}
   117  
   118  	return opts, nil
   119  }
   120  
   121  func canonicalizeCapName(s string) string {
   122  	if s == "" {
   123  		return ""
   124  	}
   125  	s = strings.ToUpper(s)
   126  	if !strings.HasPrefix(s, "CAP_") {
   127  		s = "CAP_" + s
   128  	}
   129  	if !isKnownCapName(s) {
   130  		log.L.Warnf("unknown capability name %q", s)
   131  		// Not a fatal error, because runtime might be aware of this cap
   132  	}
   133  	return s
   134  }
   135  
   136  var (
   137  	knownCapNames     map[string]struct{}
   138  	knownCapNamesOnce sync.Once
   139  )
   140  
   141  func isKnownCapName(s string) bool {
   142  	knownCapNamesOnce.Do(func() {
   143  		known := cap.Known()
   144  		knownCapNames = make(map[string]struct{}, len(known))
   145  		for _, f := range known {
   146  			knownCapNames[f] = struct{}{}
   147  		}
   148  	})
   149  	_, ok := knownCapNames[s]
   150  	return ok
   151  }
   152  
   153  func generateCapOpts(capAdd, capDrop []string) ([]oci.SpecOpts, error) {
   154  	if len(capAdd) == 0 && len(capDrop) == 0 {
   155  		return nil, nil
   156  	}
   157  
   158  	var opts []oci.SpecOpts
   159  	if strutil.InStringSlice(capDrop, "ALL") {
   160  		opts = append(opts, oci.WithCapabilities(nil))
   161  	}
   162  
   163  	if strutil.InStringSlice(capAdd, "ALL") {
   164  		opts = append(opts, oci.WithAllCurrentCapabilities)
   165  	} else {
   166  		var capsAdd []string
   167  		for _, c := range capAdd {
   168  			capsAdd = append(capsAdd, canonicalizeCapName(c))
   169  		}
   170  		opts = append(opts, oci.WithAddedCapabilities(capsAdd))
   171  	}
   172  
   173  	if !strutil.InStringSlice(capDrop, "ALL") {
   174  		var capsDrop []string
   175  		for _, c := range capDrop {
   176  			capsDrop = append(capsDrop, canonicalizeCapName(c))
   177  		}
   178  		opts = append(opts, oci.WithDroppedCapabilities(capsDrop))
   179  	}
   180  	return opts, nil
   181  }