734
									
								
								vendor/github.com/opencontainers/runtime-tools/validate/validate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										734
									
								
								vendor/github.com/opencontainers/runtime-tools/validate/validate.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -13,40 +13,47 @@ import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unicode"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/blang/semver"
 | 
			
		||||
	"github.com/hashicorp/go-multierror"
 | 
			
		||||
	rspec "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	osFilepath "github.com/opencontainers/runtime-tools/filepath"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/syndtr/gocapability/capability"
 | 
			
		||||
 | 
			
		||||
	"github.com/opencontainers/runtime-tools/specerror"
 | 
			
		||||
	"github.com/xeipuuv/gojsonschema"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const specConfig = "config.json"
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	defaultRlimits = []string{
 | 
			
		||||
	// http://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html
 | 
			
		||||
	posixRlimits = []string{
 | 
			
		||||
		"RLIMIT_AS",
 | 
			
		||||
		"RLIMIT_CORE",
 | 
			
		||||
		"RLIMIT_CPU",
 | 
			
		||||
		"RLIMIT_DATA",
 | 
			
		||||
		"RLIMIT_FSIZE",
 | 
			
		||||
		"RLIMIT_LOCKS",
 | 
			
		||||
		"RLIMIT_NOFILE",
 | 
			
		||||
		"RLIMIT_STACK",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/getrlimit.2?h=man-pages-4.13
 | 
			
		||||
	linuxRlimits = append(posixRlimits, []string{
 | 
			
		||||
		"RLIMIT_MEMLOCK",
 | 
			
		||||
		"RLIMIT_MSGQUEUE",
 | 
			
		||||
		"RLIMIT_NICE",
 | 
			
		||||
		"RLIMIT_NOFILE",
 | 
			
		||||
		"RLIMIT_NPROC",
 | 
			
		||||
		"RLIMIT_RSS",
 | 
			
		||||
		"RLIMIT_RTPRIO",
 | 
			
		||||
		"RLIMIT_RTTIME",
 | 
			
		||||
		"RLIMIT_SIGPENDING",
 | 
			
		||||
		"RLIMIT_STACK",
 | 
			
		||||
	}
 | 
			
		||||
	}...)
 | 
			
		||||
 | 
			
		||||
	configSchemaTemplate = "https://raw.githubusercontent.com/opencontainers/runtime-spec/v%s/schema/config-schema.json"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Validator represents a validator for runtime bundle
 | 
			
		||||
@@ -58,23 +65,20 @@ type Validator struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewValidator creates a Validator
 | 
			
		||||
func NewValidator(spec *rspec.Spec, bundlePath string, hostSpecific bool, platform string) Validator {
 | 
			
		||||
func NewValidator(spec *rspec.Spec, bundlePath string, hostSpecific bool, platform string) (Validator, error) {
 | 
			
		||||
	if hostSpecific && platform != runtime.GOOS {
 | 
			
		||||
		platform = runtime.GOOS
 | 
			
		||||
		return Validator{}, fmt.Errorf("When hostSpecific is set, platform must be same as the host platform")
 | 
			
		||||
	}
 | 
			
		||||
	return Validator{
 | 
			
		||||
		spec:         spec,
 | 
			
		||||
		bundlePath:   bundlePath,
 | 
			
		||||
		HostSpecific: hostSpecific,
 | 
			
		||||
		platform:     platform,
 | 
			
		||||
	}
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewValidatorFromPath creates a Validator with specified bundle path
 | 
			
		||||
func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string) (Validator, error) {
 | 
			
		||||
	if hostSpecific && platform != runtime.GOOS {
 | 
			
		||||
		platform = runtime.GOOS
 | 
			
		||||
	}
 | 
			
		||||
	if bundlePath == "" {
 | 
			
		||||
		return Validator{}, fmt.Errorf("bundle path shouldn't be empty")
 | 
			
		||||
	}
 | 
			
		||||
@@ -86,7 +90,7 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
 | 
			
		||||
	configPath := filepath.Join(bundlePath, specConfig)
 | 
			
		||||
	content, err := ioutil.ReadFile(configPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Validator{}, specerror.NewError(specerror.ConfigFileExistence, err, rspec.Version)
 | 
			
		||||
		return Validator{}, specerror.NewError(specerror.ConfigInRootBundleDir, err, rspec.Version)
 | 
			
		||||
	}
 | 
			
		||||
	if !utf8.Valid(content) {
 | 
			
		||||
		return Validator{}, fmt.Errorf("%q is not encoded in UTF-8", configPath)
 | 
			
		||||
@@ -96,38 +100,109 @@ func NewValidatorFromPath(bundlePath string, hostSpecific bool, platform string)
 | 
			
		||||
		return Validator{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NewValidator(&spec, bundlePath, hostSpecific, platform), nil
 | 
			
		||||
	return NewValidator(&spec, bundlePath, hostSpecific, platform)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckAll checks all parts of runtime bundle
 | 
			
		||||
func (v *Validator) CheckAll() error {
 | 
			
		||||
	var errs *multierror.Error
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckJSONSchema())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckPlatform())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckRoot())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckMandatoryFields())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckSemVer())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckMounts())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckProcess())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckHooks())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckLinux())
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckAnnotations())
 | 
			
		||||
	if v.platform == "linux" || v.platform == "solaris" {
 | 
			
		||||
		errs = multierror.Append(errs, v.CheckHooks())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errs.ErrorOrNil()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// JSONSchemaURL returns the URL for the JSON Schema specifying the
 | 
			
		||||
// configuration format.  It consumes configSchemaTemplate, but we
 | 
			
		||||
// provide it as a function to isolate consumers from inconsistent
 | 
			
		||||
// naming as runtime-spec evolves.
 | 
			
		||||
func JSONSchemaURL(version string) (url string, err error) {
 | 
			
		||||
	ver, err := semver.Parse(version)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", specerror.NewError(specerror.SpecVersionInSemVer, err, rspec.Version)
 | 
			
		||||
	}
 | 
			
		||||
	configRenamedToConfigSchemaVersion, err := semver.Parse("1.0.0-rc2") // config.json became config-schema.json in 1.0.0-rc2
 | 
			
		||||
	if ver.Compare(configRenamedToConfigSchemaVersion) == -1 {
 | 
			
		||||
		return "", fmt.Errorf("unsupported configuration version (older than %s)", configRenamedToConfigSchemaVersion)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf(configSchemaTemplate, version), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckJSONSchema validates the configuration against the
 | 
			
		||||
// runtime-spec JSON Schema, using the version of the schema that
 | 
			
		||||
// matches the configuration's declared version.
 | 
			
		||||
func (v *Validator) CheckJSONSchema() (errs error) {
 | 
			
		||||
	logrus.Debugf("check JSON schema")
 | 
			
		||||
 | 
			
		||||
	url, err := JSONSchemaURL(v.spec.Version)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = multierror.Append(errs, err)
 | 
			
		||||
		return errs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	schemaLoader := gojsonschema.NewReferenceLoader(url)
 | 
			
		||||
	documentLoader := gojsonschema.NewGoLoader(v.spec)
 | 
			
		||||
	result, err := gojsonschema.Validate(schemaLoader, documentLoader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = multierror.Append(errs, err)
 | 
			
		||||
		return errs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !result.Valid() {
 | 
			
		||||
		for _, resultError := range result.Errors() {
 | 
			
		||||
			errs = multierror.Append(errs, errors.New(resultError.String()))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckRoot checks status of v.spec.Root
 | 
			
		||||
func (v *Validator) CheckRoot() (errs error) {
 | 
			
		||||
	logrus.Debugf("check root")
 | 
			
		||||
 | 
			
		||||
	if v.platform == "windows" && v.spec.Windows != nil && v.spec.Windows.HyperV != nil {
 | 
			
		||||
		if v.spec.Root != nil {
 | 
			
		||||
	if v.platform == "windows" && v.spec.Windows != nil {
 | 
			
		||||
		if v.spec.Windows.HyperV != nil {
 | 
			
		||||
			if v.spec.Root != nil {
 | 
			
		||||
				errs = multierror.Append(errs,
 | 
			
		||||
					specerror.NewError(specerror.RootOnHyperVNotSet, fmt.Errorf("for Hyper-V containers, Root must not be set"), rspec.Version))
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		} else if v.spec.Root == nil {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(specerror.RootOnHyperV, fmt.Errorf("for Hyper-V containers, Root must not be set"), rspec.Version))
 | 
			
		||||
				specerror.NewError(specerror.RootOnWindowsRequired, fmt.Errorf("on Windows, for Windows Server Containers, this field is REQUIRED"), rspec.Version))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	} else if v.spec.Root == nil {
 | 
			
		||||
	} else if v.platform != "windows" && v.spec.Root == nil {
 | 
			
		||||
		errs = multierror.Append(errs,
 | 
			
		||||
			specerror.NewError(specerror.RootOnNonHyperV, fmt.Errorf("for non-Hyper-V containers, Root must be set"), rspec.Version))
 | 
			
		||||
			specerror.NewError(specerror.RootOnNonWindowsRequired, fmt.Errorf("on all other platforms, this field is REQUIRED"), rspec.Version))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.platform == "windows" {
 | 
			
		||||
		matched, err := regexp.MatchString(`\\\\[?]\\Volume[{][a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}[}]\\`, v.spec.Root.Path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			errs = multierror.Append(errs, err)
 | 
			
		||||
		} else if !matched {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(specerror.RootPathOnWindowsGUID, fmt.Errorf("root.path is %q, but it MUST be a volume GUID path when target platform is windows", v.spec.Root.Path), rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if v.spec.Root.Readonly {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(specerror.RootReadonlyOnWindowsFalse, fmt.Errorf("root.readonly field MUST be omitted or false when target platform is windows"), rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -139,7 +214,7 @@ func (v *Validator) CheckRoot() (errs error) {
 | 
			
		||||
 | 
			
		||||
	if filepath.Base(v.spec.Root.Path) != "rootfs" {
 | 
			
		||||
		errs = multierror.Append(errs,
 | 
			
		||||
			specerror.NewError(specerror.PathName, fmt.Errorf("path name should be the conventional 'rootfs'"), rspec.Version))
 | 
			
		||||
			specerror.NewError(specerror.RootPathOnPosixConvention, fmt.Errorf("path name should be the conventional 'rootfs'"), rspec.Version))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var rootfsPath string
 | 
			
		||||
@@ -159,10 +234,10 @@ func (v *Validator) CheckRoot() (errs error) {
 | 
			
		||||
 | 
			
		||||
	if fi, err := os.Stat(rootfsPath); err != nil {
 | 
			
		||||
		errs = multierror.Append(errs,
 | 
			
		||||
			specerror.NewError(specerror.PathExistence, fmt.Errorf("cannot find the root path %q", rootfsPath), rspec.Version))
 | 
			
		||||
			specerror.NewError(specerror.RootPathExist, fmt.Errorf("cannot find the root path %q", rootfsPath), rspec.Version))
 | 
			
		||||
	} else if !fi.IsDir() {
 | 
			
		||||
		errs = multierror.Append(errs,
 | 
			
		||||
			specerror.NewError(specerror.PathExistence, fmt.Errorf("root.path %q is not a directory", rootfsPath), rspec.Version))
 | 
			
		||||
			specerror.NewError(specerror.RootPathExist, fmt.Errorf("root.path %q is not a directory", rootfsPath), rspec.Version))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rootParent := filepath.Dir(absRootPath)
 | 
			
		||||
@@ -171,13 +246,6 @@ func (v *Validator) CheckRoot() (errs error) {
 | 
			
		||||
			specerror.NewError(specerror.ArtifactsInSingleDir, fmt.Errorf("root.path is %q, but it MUST be a child of %q", v.spec.Root.Path, absBundlePath), rspec.Version))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.platform == "windows" {
 | 
			
		||||
		if v.spec.Root.Readonly {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(specerror.ReadonlyOnWindows, fmt.Errorf("root.readonly field MUST be omitted or false when target platform is windows"), rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -189,7 +257,7 @@ func (v *Validator) CheckSemVer() (errs error) {
 | 
			
		||||
	_, err := semver.Parse(version)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		errs = multierror.Append(errs,
 | 
			
		||||
			specerror.NewError(specerror.SpecVersion, fmt.Errorf("%q is not valid SemVer: %s", version, err.Error()), rspec.Version))
 | 
			
		||||
			specerror.NewError(specerror.SpecVersionInSemVer, fmt.Errorf("%q is not valid SemVer: %s", version, err.Error()), rspec.Version))
 | 
			
		||||
	}
 | 
			
		||||
	if version != rspec.Version {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("validate currently only handles version %s, but the supplied configuration targets %s", rspec.Version, version))
 | 
			
		||||
@@ -202,19 +270,29 @@ func (v *Validator) CheckSemVer() (errs error) {
 | 
			
		||||
func (v *Validator) CheckHooks() (errs error) {
 | 
			
		||||
	logrus.Debugf("check hooks")
 | 
			
		||||
 | 
			
		||||
	if v.platform != "linux" && v.platform != "solaris" {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("For %q platform, the configuration structure does not support hooks", v.platform))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.spec.Hooks != nil {
 | 
			
		||||
		errs = multierror.Append(errs, checkEventHooks("pre-start", v.spec.Hooks.Prestart, v.HostSpecific))
 | 
			
		||||
		errs = multierror.Append(errs, checkEventHooks("post-start", v.spec.Hooks.Poststart, v.HostSpecific))
 | 
			
		||||
		errs = multierror.Append(errs, checkEventHooks("post-stop", v.spec.Hooks.Poststop, v.HostSpecific))
 | 
			
		||||
		errs = multierror.Append(errs, v.checkEventHooks("prestart", v.spec.Hooks.Prestart, v.HostSpecific))
 | 
			
		||||
		errs = multierror.Append(errs, v.checkEventHooks("poststart", v.spec.Hooks.Poststart, v.HostSpecific))
 | 
			
		||||
		errs = multierror.Append(errs, v.checkEventHooks("poststop", v.spec.Hooks.Poststop, v.HostSpecific))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (errs error) {
 | 
			
		||||
	for _, hook := range hooks {
 | 
			
		||||
		if !filepath.IsAbs(hook.Path) {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("the %s hook %v: is not absolute path", hookType, hook.Path))
 | 
			
		||||
func (v *Validator) checkEventHooks(hookType string, hooks []rspec.Hook, hostSpecific bool) (errs error) {
 | 
			
		||||
	for i, hook := range hooks {
 | 
			
		||||
		if !osFilepath.IsAbs(v.platform, hook.Path) {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(
 | 
			
		||||
					specerror.PosixHooksPathAbs,
 | 
			
		||||
					fmt.Errorf("hooks.%s[%d].path %v: is not absolute path",
 | 
			
		||||
						hookType, i, hook.Path),
 | 
			
		||||
					rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if hostSpecific {
 | 
			
		||||
@@ -246,8 +324,12 @@ func (v *Validator) CheckProcess() (errs error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	process := v.spec.Process
 | 
			
		||||
	if !filepath.IsAbs(process.Cwd) {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("cwd %q is not an absolute path", process.Cwd))
 | 
			
		||||
	if !osFilepath.IsAbs(v.platform, process.Cwd) {
 | 
			
		||||
		errs = multierror.Append(errs,
 | 
			
		||||
			specerror.NewError(
 | 
			
		||||
				specerror.ProcCwdAbs,
 | 
			
		||||
				fmt.Errorf("cwd %q is not an absolute path", process.Cwd),
 | 
			
		||||
				rspec.Version))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, env := range process.Env {
 | 
			
		||||
@@ -257,9 +339,13 @@ func (v *Validator) CheckProcess() (errs error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(process.Args) == 0 {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("args must not be empty"))
 | 
			
		||||
		errs = multierror.Append(errs,
 | 
			
		||||
			specerror.NewError(
 | 
			
		||||
				specerror.ProcArgsOneEntryRequired,
 | 
			
		||||
				fmt.Errorf("args must not be empty"),
 | 
			
		||||
				rspec.Version))
 | 
			
		||||
	} else {
 | 
			
		||||
		if filepath.IsAbs(process.Args[0]) {
 | 
			
		||||
		if filepath.IsAbs(process.Args[0]) && v.spec.Root != nil {
 | 
			
		||||
			var rootfsPath string
 | 
			
		||||
			if filepath.IsAbs(v.spec.Root.Path) {
 | 
			
		||||
				rootfsPath = v.spec.Root.Path
 | 
			
		||||
@@ -281,12 +367,15 @@ func (v *Validator) CheckProcess() (errs error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.spec.Process.Capabilities != nil {
 | 
			
		||||
		errs = multierror.Append(errs, v.CheckCapabilities())
 | 
			
		||||
	if v.platform == "linux" || v.platform == "solaris" {
 | 
			
		||||
		errs = multierror.Append(errs, v.CheckRlimits())
 | 
			
		||||
	}
 | 
			
		||||
	errs = multierror.Append(errs, v.CheckRlimits())
 | 
			
		||||
 | 
			
		||||
	if v.platform == "linux" {
 | 
			
		||||
		if v.spec.Process.Capabilities != nil {
 | 
			
		||||
			errs = multierror.Append(errs, v.CheckCapabilities())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(process.ApparmorProfile) > 0 {
 | 
			
		||||
			profilePath := filepath.Join(v.bundlePath, v.spec.Root.Path, "/etc/apparmor.d", process.ApparmorProfile)
 | 
			
		||||
			_, err := os.Stat(profilePath)
 | 
			
		||||
@@ -301,60 +390,61 @@ func (v *Validator) CheckProcess() (errs error) {
 | 
			
		||||
 | 
			
		||||
// CheckCapabilities checks v.spec.Process.Capabilities
 | 
			
		||||
func (v *Validator) CheckCapabilities() (errs error) {
 | 
			
		||||
	if v.platform != "linux" {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("For %q platform, the configuration structure does not support process.capabilities", v.platform))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	process := v.spec.Process
 | 
			
		||||
	if v.platform == "linux" {
 | 
			
		||||
		var effective, permitted, inheritable, ambient bool
 | 
			
		||||
		caps := make(map[string][]string)
 | 
			
		||||
	var effective, permitted, inheritable, ambient bool
 | 
			
		||||
	caps := make(map[string][]string)
 | 
			
		||||
 | 
			
		||||
		for _, cap := range process.Capabilities.Bounding {
 | 
			
		||||
			caps[cap] = append(caps[cap], "bounding")
 | 
			
		||||
		}
 | 
			
		||||
		for _, cap := range process.Capabilities.Effective {
 | 
			
		||||
			caps[cap] = append(caps[cap], "effective")
 | 
			
		||||
		}
 | 
			
		||||
		for _, cap := range process.Capabilities.Inheritable {
 | 
			
		||||
			caps[cap] = append(caps[cap], "inheritable")
 | 
			
		||||
		}
 | 
			
		||||
		for _, cap := range process.Capabilities.Permitted {
 | 
			
		||||
			caps[cap] = append(caps[cap], "permitted")
 | 
			
		||||
		}
 | 
			
		||||
		for _, cap := range process.Capabilities.Ambient {
 | 
			
		||||
			caps[cap] = append(caps[cap], "ambient")
 | 
			
		||||
	for _, cap := range process.Capabilities.Bounding {
 | 
			
		||||
		caps[cap] = append(caps[cap], "bounding")
 | 
			
		||||
	}
 | 
			
		||||
	for _, cap := range process.Capabilities.Effective {
 | 
			
		||||
		caps[cap] = append(caps[cap], "effective")
 | 
			
		||||
	}
 | 
			
		||||
	for _, cap := range process.Capabilities.Inheritable {
 | 
			
		||||
		caps[cap] = append(caps[cap], "inheritable")
 | 
			
		||||
	}
 | 
			
		||||
	for _, cap := range process.Capabilities.Permitted {
 | 
			
		||||
		caps[cap] = append(caps[cap], "permitted")
 | 
			
		||||
	}
 | 
			
		||||
	for _, cap := range process.Capabilities.Ambient {
 | 
			
		||||
		caps[cap] = append(caps[cap], "ambient")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for capability, owns := range caps {
 | 
			
		||||
		if err := CapValid(capability, v.HostSpecific); err != nil {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("capability %q is not valid, man capabilities(7)", capability))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for capability, owns := range caps {
 | 
			
		||||
			if err := CapValid(capability, v.HostSpecific); err != nil {
 | 
			
		||||
				errs = multierror.Append(errs, fmt.Errorf("capability %q is not valid, man capabilities(7)", capability))
 | 
			
		||||
		effective, permitted, ambient, inheritable = false, false, false, false
 | 
			
		||||
		for _, set := range owns {
 | 
			
		||||
			if set == "effective" {
 | 
			
		||||
				effective = true
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			effective, permitted, ambient, inheritable = false, false, false, false
 | 
			
		||||
			for _, set := range owns {
 | 
			
		||||
				if set == "effective" {
 | 
			
		||||
					effective = true
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if set == "inheritable" {
 | 
			
		||||
					inheritable = true
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if set == "permitted" {
 | 
			
		||||
					permitted = true
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if set == "ambient" {
 | 
			
		||||
					ambient = true
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			if set == "inheritable" {
 | 
			
		||||
				inheritable = true
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if effective && !permitted {
 | 
			
		||||
				errs = multierror.Append(errs, fmt.Errorf("effective capability %q is not allowed, as it's not permitted", capability))
 | 
			
		||||
			if set == "permitted" {
 | 
			
		||||
				permitted = true
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if ambient && !(effective && inheritable) {
 | 
			
		||||
				errs = multierror.Append(errs, fmt.Errorf("ambient capability %q is not allowed, as it's not permitted and inheribate", capability))
 | 
			
		||||
			if set == "ambient" {
 | 
			
		||||
				ambient = true
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		logrus.Warnf("process.capabilities validation not yet implemented for OS %q", v.platform)
 | 
			
		||||
		if effective && !permitted {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("effective capability %q is not allowed, as it's not permitted", capability))
 | 
			
		||||
		}
 | 
			
		||||
		if ambient && !(permitted && inheritable) {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("ambient capability %q is not allowed, as it's not permitted and inheribate", capability))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
@@ -362,11 +452,21 @@ func (v *Validator) CheckCapabilities() (errs error) {
 | 
			
		||||
 | 
			
		||||
// CheckRlimits checks v.spec.Process.Rlimits
 | 
			
		||||
func (v *Validator) CheckRlimits() (errs error) {
 | 
			
		||||
	if v.platform != "linux" && v.platform != "solaris" {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("For %q platform, the configuration structure does not support process.rlimits", v.platform))
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	process := v.spec.Process
 | 
			
		||||
	for index, rlimit := range process.Rlimits {
 | 
			
		||||
		for i := index + 1; i < len(process.Rlimits); i++ {
 | 
			
		||||
			if process.Rlimits[index].Type == process.Rlimits[i].Type {
 | 
			
		||||
				errs = multierror.Append(errs, fmt.Errorf("rlimit can not contain the same type %q", process.Rlimits[index].Type))
 | 
			
		||||
				errs = multierror.Append(errs,
 | 
			
		||||
					specerror.NewError(
 | 
			
		||||
						specerror.PosixProcRlimitsErrorOnDup,
 | 
			
		||||
						fmt.Errorf("rlimit can not contain the same type %q",
 | 
			
		||||
							process.Rlimits[index].Type),
 | 
			
		||||
						rspec.Version))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		errs = multierror.Append(errs, v.rlimitValid(rlimit))
 | 
			
		||||
@@ -430,31 +530,33 @@ func (v *Validator) CheckMounts() (errs error) {
 | 
			
		||||
		if supportedTypes != nil && !supportedTypes[mountA.Type] {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("unsupported mount type %q", mountA.Type))
 | 
			
		||||
		}
 | 
			
		||||
		if v.platform == "windows" {
 | 
			
		||||
			if err := pathValid(v.platform, mountA.Destination); err != nil {
 | 
			
		||||
				errs = multierror.Append(errs, err)
 | 
			
		||||
			}
 | 
			
		||||
			if err := pathValid(v.platform, mountA.Source); err != nil {
 | 
			
		||||
				errs = multierror.Append(errs, err)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if err := pathValid(v.platform, mountA.Destination); err != nil {
 | 
			
		||||
				errs = multierror.Append(errs, err)
 | 
			
		||||
			}
 | 
			
		||||
		if !osFilepath.IsAbs(v.platform, mountA.Destination) {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(
 | 
			
		||||
					specerror.MountsDestAbs,
 | 
			
		||||
					fmt.Errorf("mounts[%d].destination %q is not absolute",
 | 
			
		||||
						i,
 | 
			
		||||
						mountA.Destination),
 | 
			
		||||
					rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
		for j, mountB := range v.spec.Mounts {
 | 
			
		||||
			if i == j {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// whether B.Desination is nested within A.Destination
 | 
			
		||||
			nested, err := nestedValid(v.platform, mountA.Destination, mountB.Destination)
 | 
			
		||||
			nested, err := osFilepath.IsAncestor(v.platform, mountA.Destination, mountB.Destination, ".")
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				errs = multierror.Append(errs, err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if nested {
 | 
			
		||||
				if v.platform == "windows" && i < j {
 | 
			
		||||
					errs = multierror.Append(errs, fmt.Errorf("on Windows, %v nested within %v is forbidden", mountB.Destination, mountA.Destination))
 | 
			
		||||
					errs = multierror.Append(errs,
 | 
			
		||||
						specerror.NewError(
 | 
			
		||||
							specerror.MountsDestOnWindowsNotNested,
 | 
			
		||||
							fmt.Errorf("on Windows, %v nested within %v is forbidden",
 | 
			
		||||
								mountB.Destination, mountA.Destination),
 | 
			
		||||
							rspec.Version))
 | 
			
		||||
				}
 | 
			
		||||
				if i > j {
 | 
			
		||||
					logrus.Warnf("%v will be covered by %v", mountB.Destination, mountA.Destination)
 | 
			
		||||
@@ -475,200 +577,18 @@ func (v *Validator) CheckPlatform() (errs error) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.HostSpecific && v.platform != runtime.GOOS {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("platform %q differs from the host %q, skipping host-specific checks", v.platform, runtime.GOOS))
 | 
			
		||||
		v.HostSpecific = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.platform == "windows" {
 | 
			
		||||
		if v.spec.Windows == nil {
 | 
			
		||||
			errs = multierror.Append(errs, errors.New("'windows' MUST be set when platform is `windows`"))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckLinux checks v.spec.Linux
 | 
			
		||||
func (v *Validator) CheckLinux() (errs error) {
 | 
			
		||||
	logrus.Debugf("check linux")
 | 
			
		||||
 | 
			
		||||
	if v.spec.Linux == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var nsTypeList = map[rspec.LinuxNamespaceType]struct {
 | 
			
		||||
		num      int
 | 
			
		||||
		newExist bool
 | 
			
		||||
	}{
 | 
			
		||||
		rspec.PIDNamespace:     {0, false},
 | 
			
		||||
		rspec.NetworkNamespace: {0, false},
 | 
			
		||||
		rspec.MountNamespace:   {0, false},
 | 
			
		||||
		rspec.IPCNamespace:     {0, false},
 | 
			
		||||
		rspec.UTSNamespace:     {0, false},
 | 
			
		||||
		rspec.UserNamespace:    {0, false},
 | 
			
		||||
		rspec.CgroupNamespace:  {0, false},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for index := 0; index < len(v.spec.Linux.Namespaces); index++ {
 | 
			
		||||
		ns := v.spec.Linux.Namespaces[index]
 | 
			
		||||
		if !namespaceValid(ns) {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("namespace %v is invalid", ns))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		tmpItem := nsTypeList[ns.Type]
 | 
			
		||||
		tmpItem.num = tmpItem.num + 1
 | 
			
		||||
		if tmpItem.num > 1 {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("duplicated namespace %q", ns.Type))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(ns.Path) == 0 {
 | 
			
		||||
			tmpItem.newExist = true
 | 
			
		||||
		}
 | 
			
		||||
		nsTypeList[ns.Type] = tmpItem
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (len(v.spec.Linux.UIDMappings) > 0 || len(v.spec.Linux.GIDMappings) > 0) && !nsTypeList[rspec.UserNamespace].newExist {
 | 
			
		||||
		errs = multierror.Append(errs, errors.New("the UID/GID mappings requires a new User namespace to be specified as well"))
 | 
			
		||||
	} else if len(v.spec.Linux.UIDMappings) > 5 {
 | 
			
		||||
		errs = multierror.Append(errs, errors.New("only 5 UID mappings are allowed (linux kernel restriction)"))
 | 
			
		||||
	} else if len(v.spec.Linux.GIDMappings) > 5 {
 | 
			
		||||
		errs = multierror.Append(errs, errors.New("only 5 GID mappings are allowed (linux kernel restriction)"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k := range v.spec.Linux.Sysctl {
 | 
			
		||||
		if strings.HasPrefix(k, "net.") && !nsTypeList[rspec.NetworkNamespace].newExist {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("sysctl %v requires a new Network namespace to be specified as well", k))
 | 
			
		||||
		}
 | 
			
		||||
		if strings.HasPrefix(k, "fs.mqueue.") {
 | 
			
		||||
			if !nsTypeList[rspec.MountNamespace].newExist || !nsTypeList[rspec.IPCNamespace].newExist {
 | 
			
		||||
				errs = multierror.Append(errs, fmt.Errorf("sysctl %v requires a new IPC namespace and Mount namespace to be specified as well", k))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.platform == "linux" && !nsTypeList[rspec.UTSNamespace].newExist && v.spec.Hostname != "" {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("on Linux, hostname requires a new UTS namespace to be specified as well"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Linux devices validation
 | 
			
		||||
	devList := make(map[string]bool)
 | 
			
		||||
	devTypeList := make(map[string]bool)
 | 
			
		||||
	for index := 0; index < len(v.spec.Linux.Devices); index++ {
 | 
			
		||||
		device := v.spec.Linux.Devices[index]
 | 
			
		||||
		if !deviceValid(device) {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("device %v is invalid", device))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, exists := devList[device.Path]; exists {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("device %s is duplicated", device.Path))
 | 
			
		||||
		} else {
 | 
			
		||||
			var rootfsPath string
 | 
			
		||||
			if filepath.IsAbs(v.spec.Root.Path) {
 | 
			
		||||
				rootfsPath = v.spec.Root.Path
 | 
			
		||||
			} else {
 | 
			
		||||
				rootfsPath = filepath.Join(v.bundlePath, v.spec.Root.Path)
 | 
			
		||||
			}
 | 
			
		||||
			absPath := filepath.Join(rootfsPath, device.Path)
 | 
			
		||||
			fi, err := os.Stat(absPath)
 | 
			
		||||
			if os.IsNotExist(err) {
 | 
			
		||||
				devList[device.Path] = true
 | 
			
		||||
			} else if err != nil {
 | 
			
		||||
				errs = multierror.Append(errs, err)
 | 
			
		||||
			} else {
 | 
			
		||||
				fStat, ok := fi.Sys().(*syscall.Stat_t)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					errs = multierror.Append(errs, fmt.Errorf("cannot determine state for device %s", device.Path))
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				var devType string
 | 
			
		||||
				switch fStat.Mode & syscall.S_IFMT {
 | 
			
		||||
				case syscall.S_IFCHR:
 | 
			
		||||
					devType = "c"
 | 
			
		||||
				case syscall.S_IFBLK:
 | 
			
		||||
					devType = "b"
 | 
			
		||||
				case syscall.S_IFIFO:
 | 
			
		||||
					devType = "p"
 | 
			
		||||
				default:
 | 
			
		||||
					devType = "unmatched"
 | 
			
		||||
				}
 | 
			
		||||
				if devType != device.Type || (devType == "c" && device.Type == "u") {
 | 
			
		||||
					errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if devType != "p" {
 | 
			
		||||
					dev := fStat.Rdev
 | 
			
		||||
					major := (dev >> 8) & 0xfff
 | 
			
		||||
					minor := (dev & 0xff) | ((dev >> 12) & 0xfff00)
 | 
			
		||||
					if int64(major) != device.Major || int64(minor) != device.Minor {
 | 
			
		||||
						errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if device.FileMode != nil {
 | 
			
		||||
					expectedPerm := *device.FileMode & os.ModePerm
 | 
			
		||||
					actualPerm := fi.Mode() & os.ModePerm
 | 
			
		||||
					if expectedPerm != actualPerm {
 | 
			
		||||
						errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if device.UID != nil {
 | 
			
		||||
					if *device.UID != fStat.Uid {
 | 
			
		||||
						errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				if device.GID != nil {
 | 
			
		||||
					if *device.GID != fStat.Gid {
 | 
			
		||||
						errs = multierror.Append(errs, fmt.Errorf("unmatched %s already exists in filesystem", device.Path))
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// unify u->c when comparing, they are synonyms
 | 
			
		||||
		var devID string
 | 
			
		||||
		if device.Type == "u" {
 | 
			
		||||
			devID = fmt.Sprintf("%s:%d:%d", "c", device.Major, device.Minor)
 | 
			
		||||
		} else {
 | 
			
		||||
			devID = fmt.Sprintf("%s:%d:%d", device.Type, device.Major, device.Minor)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, exists := devTypeList[devID]; exists {
 | 
			
		||||
			logrus.Warnf("type:%s, major:%d and minor:%d for linux devices is duplicated", device.Type, device.Major, device.Minor)
 | 
			
		||||
		} else {
 | 
			
		||||
			devTypeList[devID] = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.spec.Linux.Resources != nil {
 | 
			
		||||
		errs = multierror.Append(errs, v.CheckLinuxResources())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.spec.Linux.Seccomp != nil {
 | 
			
		||||
		errs = multierror.Append(errs, v.CheckSeccomp())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch v.spec.Linux.RootfsPropagation {
 | 
			
		||||
	case "":
 | 
			
		||||
	case "private":
 | 
			
		||||
	case "rprivate":
 | 
			
		||||
	case "slave":
 | 
			
		||||
	case "rslave":
 | 
			
		||||
	case "shared":
 | 
			
		||||
	case "rshared":
 | 
			
		||||
	case "unbindable":
 | 
			
		||||
	case "runbindable":
 | 
			
		||||
	default:
 | 
			
		||||
		errs = multierror.Append(errs, errors.New("rootfsPropagation must be empty or one of \"private|rprivate|slave|rslave|shared|rshared|unbindable|runbindable\""))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, maskedPath := range v.spec.Linux.MaskedPaths {
 | 
			
		||||
		if !strings.HasPrefix(maskedPath, "/") {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("maskedPath %v is not an absolute path", maskedPath))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, readonlyPath := range v.spec.Linux.ReadonlyPaths {
 | 
			
		||||
		if !strings.HasPrefix(readonlyPath, "/") {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("readonlyPath %v is not an absolute path", readonlyPath))
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(
 | 
			
		||||
					specerror.PlatformSpecConfOnWindowsSet,
 | 
			
		||||
					fmt.Errorf("'windows' MUST be set when platform is `windows`"),
 | 
			
		||||
					rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -710,7 +630,7 @@ func (v *Validator) CheckLinuxResources() (errs error) {
 | 
			
		||||
	}
 | 
			
		||||
	for index := 0; index < len(r.Devices); index++ {
 | 
			
		||||
		switch r.Devices[index].Type {
 | 
			
		||||
		case "a", "b", "c":
 | 
			
		||||
		case "a", "b", "c", "":
 | 
			
		||||
		default:
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("type of devices %s is invalid", r.Devices[index].Type))
 | 
			
		||||
		}
 | 
			
		||||
@@ -726,44 +646,41 @@ func (v *Validator) CheckLinuxResources() (errs error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r.BlockIO != nil && r.BlockIO.WeightDevice != nil {
 | 
			
		||||
		for i, weightDevice := range r.BlockIO.WeightDevice {
 | 
			
		||||
			if weightDevice.Weight == nil && weightDevice.LeafWeight == nil {
 | 
			
		||||
				errs = multierror.Append(errs,
 | 
			
		||||
					specerror.NewError(
 | 
			
		||||
						specerror.BlkIOWeightOrLeafWeightExist,
 | 
			
		||||
						fmt.Errorf("linux.resources.blockIO.weightDevice[%d] specifies neither weight nor leafWeight", i),
 | 
			
		||||
						rspec.Version))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckSeccomp checkc v.spec.Linux.Seccomp
 | 
			
		||||
func (v *Validator) CheckSeccomp() (errs error) {
 | 
			
		||||
	logrus.Debugf("check linux seccomp")
 | 
			
		||||
// CheckAnnotations checks v.spec.Annotations
 | 
			
		||||
func (v *Validator) CheckAnnotations() (errs error) {
 | 
			
		||||
	logrus.Debugf("check annotations")
 | 
			
		||||
 | 
			
		||||
	s := v.spec.Linux.Seccomp
 | 
			
		||||
	if !seccompActionValid(s.DefaultAction) {
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("seccomp defaultAction %q is invalid", s.DefaultAction))
 | 
			
		||||
	}
 | 
			
		||||
	for index := 0; index < len(s.Syscalls); index++ {
 | 
			
		||||
		if !syscallValid(s.Syscalls[index]) {
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("syscall %v is invalid", s.Syscalls[index]))
 | 
			
		||||
	reversedDomain := regexp.MustCompile(`^[A-Za-z]{2,6}(\.[A-Za-z0-9-]{1,63})+$`)
 | 
			
		||||
	for key := range v.spec.Annotations {
 | 
			
		||||
		if strings.HasPrefix(key, "org.opencontainers") {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(
 | 
			
		||||
					specerror.AnnotationsKeyReservedNS,
 | 
			
		||||
					fmt.Errorf("key %q is reserved", key),
 | 
			
		||||
					rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for index := 0; index < len(s.Architectures); index++ {
 | 
			
		||||
		switch s.Architectures[index] {
 | 
			
		||||
		case rspec.ArchX86:
 | 
			
		||||
		case rspec.ArchX86_64:
 | 
			
		||||
		case rspec.ArchX32:
 | 
			
		||||
		case rspec.ArchARM:
 | 
			
		||||
		case rspec.ArchAARCH64:
 | 
			
		||||
		case rspec.ArchMIPS:
 | 
			
		||||
		case rspec.ArchMIPS64:
 | 
			
		||||
		case rspec.ArchMIPS64N32:
 | 
			
		||||
		case rspec.ArchMIPSEL:
 | 
			
		||||
		case rspec.ArchMIPSEL64:
 | 
			
		||||
		case rspec.ArchMIPSEL64N32:
 | 
			
		||||
		case rspec.ArchPPC:
 | 
			
		||||
		case rspec.ArchPPC64:
 | 
			
		||||
		case rspec.ArchPPC64LE:
 | 
			
		||||
		case rspec.ArchS390:
 | 
			
		||||
		case rspec.ArchS390X:
 | 
			
		||||
		case rspec.ArchPARISC:
 | 
			
		||||
		case rspec.ArchPARISC64:
 | 
			
		||||
		default:
 | 
			
		||||
			errs = multierror.Append(errs, fmt.Errorf("seccomp architecture %q is invalid", s.Architectures[index]))
 | 
			
		||||
 | 
			
		||||
		if !reversedDomain.MatchString(key) {
 | 
			
		||||
			errs = multierror.Append(errs,
 | 
			
		||||
				specerror.NewError(
 | 
			
		||||
					specerror.AnnotationsKeyReversedDomain,
 | 
			
		||||
					fmt.Errorf("key %q SHOULD be named using a reverse domain notation", key),
 | 
			
		||||
					rspec.Version))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -793,17 +710,6 @@ func CapValid(c string, hostSpecific bool) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LastCap return last cap of system
 | 
			
		||||
func LastCap() capability.Cap {
 | 
			
		||||
	last := capability.CAP_LAST_CAP
 | 
			
		||||
	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
 | 
			
		||||
	if last == capability.Cap(63) {
 | 
			
		||||
		last = capability.CAP_BLOCK_SUSPEND
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return last
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func envValid(env string) bool {
 | 
			
		||||
	items := strings.Split(env, "=")
 | 
			
		||||
	if len(items) < 2 {
 | 
			
		||||
@@ -826,12 +732,19 @@ func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (errs error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.platform == "linux" {
 | 
			
		||||
		for _, val := range defaultRlimits {
 | 
			
		||||
		for _, val := range linuxRlimits {
 | 
			
		||||
			if val == rlimit.Type {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		errs = multierror.Append(errs, fmt.Errorf("rlimit type %q is invalid", rlimit.Type))
 | 
			
		||||
		errs = multierror.Append(errs, specerror.NewError(specerror.PosixProcRlimitsTypeValueError, fmt.Errorf("rlimit type %q may not be valid", rlimit.Type), v.spec.Version))
 | 
			
		||||
	} else if v.platform == "solaris" {
 | 
			
		||||
		for _, val := range posixRlimits {
 | 
			
		||||
			if val == rlimit.Type {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		errs = multierror.Append(errs, specerror.NewError(specerror.PosixProcRlimitsTypeValueError, fmt.Errorf("rlimit type %q may not be valid", rlimit.Type), v.spec.Version))
 | 
			
		||||
	} else {
 | 
			
		||||
		logrus.Warnf("process.rlimits validation not yet implemented for platform %q", v.platform)
 | 
			
		||||
	}
 | 
			
		||||
@@ -839,135 +752,6 @@ func (v *Validator) rlimitValid(rlimit rspec.POSIXRlimit) (errs error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func namespaceValid(ns rspec.LinuxNamespace) bool {
 | 
			
		||||
	switch ns.Type {
 | 
			
		||||
	case rspec.PIDNamespace:
 | 
			
		||||
	case rspec.NetworkNamespace:
 | 
			
		||||
	case rspec.MountNamespace:
 | 
			
		||||
	case rspec.IPCNamespace:
 | 
			
		||||
	case rspec.UTSNamespace:
 | 
			
		||||
	case rspec.UserNamespace:
 | 
			
		||||
	case rspec.CgroupNamespace:
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ns.Path != "" && !filepath.IsAbs(ns.Path) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func pathValid(os, path string) error {
 | 
			
		||||
	if os == "windows" {
 | 
			
		||||
		matched, err := regexp.MatchString("^[a-zA-Z]:(\\\\[^\\\\/<>|:*?\"]+)+$", path)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if !matched {
 | 
			
		||||
			return fmt.Errorf("invalid windows path %v", path)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !filepath.IsAbs(path) {
 | 
			
		||||
		return fmt.Errorf("%v is not an absolute path", path)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Check whether pathB is nested whithin pathA
 | 
			
		||||
func nestedValid(os, pathA, pathB string) (bool, error) {
 | 
			
		||||
	if pathA == pathB {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	if pathA == "/" && pathB != "" {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var sep string
 | 
			
		||||
	if os == "windows" {
 | 
			
		||||
		sep = "\\"
 | 
			
		||||
	} else {
 | 
			
		||||
		sep = "/"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	splitedPathA := strings.Split(filepath.Clean(pathA), sep)
 | 
			
		||||
	splitedPathB := strings.Split(filepath.Clean(pathB), sep)
 | 
			
		||||
	lenA := len(splitedPathA)
 | 
			
		||||
	lenB := len(splitedPathB)
 | 
			
		||||
 | 
			
		||||
	if lenA > lenB {
 | 
			
		||||
		if (lenA - lenB) == 1 {
 | 
			
		||||
			// if pathA is longer but not end with separator
 | 
			
		||||
			if splitedPathA[lenA-1] != "" {
 | 
			
		||||
				return false, nil
 | 
			
		||||
			}
 | 
			
		||||
			splitedPathA = splitedPathA[:lenA-1]
 | 
			
		||||
		} else {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, partA := range splitedPathA {
 | 
			
		||||
		if partA != splitedPathB[i] {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deviceValid(d rspec.LinuxDevice) bool {
 | 
			
		||||
	switch d.Type {
 | 
			
		||||
	case "b", "c", "u":
 | 
			
		||||
		if d.Major <= 0 || d.Minor <= 0 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	case "p":
 | 
			
		||||
		if d.Major > 0 || d.Minor > 0 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func seccompActionValid(secc rspec.LinuxSeccompAction) bool {
 | 
			
		||||
	switch secc {
 | 
			
		||||
	case rspec.ActKill:
 | 
			
		||||
	case rspec.ActTrap:
 | 
			
		||||
	case rspec.ActErrno:
 | 
			
		||||
	case rspec.ActTrace:
 | 
			
		||||
	case rspec.ActAllow:
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func syscallValid(s rspec.LinuxSyscall) bool {
 | 
			
		||||
	if !seccompActionValid(s.Action) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	for index := 0; index < len(s.Args); index++ {
 | 
			
		||||
		arg := s.Args[index]
 | 
			
		||||
		switch arg.Op {
 | 
			
		||||
		case rspec.OpNotEqual:
 | 
			
		||||
		case rspec.OpLessThan:
 | 
			
		||||
		case rspec.OpLessEqual:
 | 
			
		||||
		case rspec.OpEqualTo:
 | 
			
		||||
		case rspec.OpGreaterEqual:
 | 
			
		||||
		case rspec.OpGreaterThan:
 | 
			
		||||
		case rspec.OpMaskedEqual:
 | 
			
		||||
		default:
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isStruct(t reflect.Type) bool {
 | 
			
		||||
	return t.Kind() == reflect.Struct
 | 
			
		||||
}
 | 
			
		||||
@@ -1046,5 +830,9 @@ func checkMandatory(obj interface{}) (errs error) {
 | 
			
		||||
func (v *Validator) CheckMandatoryFields() error {
 | 
			
		||||
	logrus.Debugf("check mandatory fields")
 | 
			
		||||
 | 
			
		||||
	if v.spec == nil {
 | 
			
		||||
		return fmt.Errorf("Spec can't be nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return checkMandatory(v.spec)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user