github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/runtime/engines/singularity/prepare.go (about)

     1  // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package singularity
     7  
     8  import (
     9  	"encoding/json"
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	specs "github.com/opencontainers/runtime-spec/specs-go"
    16  	"github.com/sylabs/singularity/internal/pkg/buildcfg"
    17  	"github.com/sylabs/singularity/internal/pkg/instance"
    18  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/config"
    19  	"github.com/sylabs/singularity/internal/pkg/runtime/engines/config/starter"
    20  	singularityConfig "github.com/sylabs/singularity/internal/pkg/runtime/engines/singularity/config"
    21  	"github.com/sylabs/singularity/internal/pkg/security"
    22  	"github.com/sylabs/singularity/internal/pkg/security/seccomp"
    23  	"github.com/sylabs/singularity/internal/pkg/syecl"
    24  	"github.com/sylabs/singularity/internal/pkg/sylog"
    25  	"github.com/sylabs/singularity/internal/pkg/util/fs"
    26  	"github.com/sylabs/singularity/internal/pkg/util/mainthread"
    27  	"github.com/sylabs/singularity/internal/pkg/util/user"
    28  	"github.com/sylabs/singularity/pkg/image"
    29  	"github.com/sylabs/singularity/pkg/util/capabilities"
    30  )
    31  
    32  // prepareUserCaps is responsible for checking that user's requested
    33  // capabilities are authorized
    34  func (e *EngineOperations) prepareUserCaps() error {
    35  	uid := os.Getuid()
    36  	commonCaps := make([]string, 0)
    37  
    38  	e.EngineConfig.OciConfig.SetProcessNoNewPrivileges(true)
    39  
    40  	file, err := os.OpenFile(buildcfg.CAPABILITY_FILE, os.O_RDONLY, 0644)
    41  	if err != nil {
    42  		return fmt.Errorf("while opening capability config file: %s", err)
    43  	}
    44  	defer file.Close()
    45  
    46  	capConfig, err := capabilities.ReadFrom(file)
    47  	if err != nil {
    48  		return fmt.Errorf("while parsing capability config data: %s", err)
    49  	}
    50  
    51  	pw, err := user.GetPwUID(uint32(uid))
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	caps, _ := capabilities.Split(e.EngineConfig.GetAddCaps())
    57  	caps = append(caps, e.EngineConfig.OciConfig.Process.Capabilities.Permitted...)
    58  
    59  	authorizedCaps, _ := capConfig.CheckUserCaps(pw.Name, caps)
    60  
    61  	if len(authorizedCaps) > 0 {
    62  		sylog.Debugf("User capabilities %v added", authorizedCaps)
    63  		commonCaps = authorizedCaps
    64  	}
    65  
    66  	groups, err := os.Getgroups()
    67  	for _, g := range groups {
    68  		gr, err := user.GetGrGID(uint32(g))
    69  		if err != nil {
    70  			sylog.Debugf("Ignoring group %d: %s", g, err)
    71  			continue
    72  		}
    73  		authorizedCaps, _ := capConfig.CheckGroupCaps(gr.Name, caps)
    74  		if len(authorizedCaps) > 0 {
    75  			sylog.Debugf("%s group capabilities %v added", gr.Name, authorizedCaps)
    76  			commonCaps = append(commonCaps, authorizedCaps...)
    77  		}
    78  	}
    79  
    80  	commonCaps = capabilities.RemoveDuplicated(commonCaps)
    81  
    82  	caps, _ = capabilities.Split(e.EngineConfig.GetDropCaps())
    83  	for _, cap := range caps {
    84  		for i, c := range commonCaps {
    85  			if c == cap {
    86  				sylog.Debugf("Capability %s dropped", cap)
    87  				commonCaps = append(commonCaps[:i], commonCaps[i+1:]...)
    88  				break
    89  			}
    90  		}
    91  	}
    92  
    93  	e.EngineConfig.OciConfig.Process.Capabilities.Permitted = commonCaps
    94  	e.EngineConfig.OciConfig.Process.Capabilities.Effective = commonCaps
    95  	e.EngineConfig.OciConfig.Process.Capabilities.Inheritable = commonCaps
    96  	e.EngineConfig.OciConfig.Process.Capabilities.Bounding = commonCaps
    97  	e.EngineConfig.OciConfig.Process.Capabilities.Ambient = commonCaps
    98  
    99  	return nil
   100  }
   101  
   102  // prepareRootCaps is responsible for setting root capabilities
   103  // based on capability/configuration files and requested capabilities
   104  func (e *EngineOperations) prepareRootCaps() error {
   105  	commonCaps := make([]string, 0)
   106  	defaultCapabilities := e.EngineConfig.File.RootDefaultCapabilities
   107  
   108  	uid := e.EngineConfig.GetTargetUID()
   109  	gids := e.EngineConfig.GetTargetGID()
   110  
   111  	if uid != 0 || len(gids) > 0 {
   112  		defaultCapabilities = "no"
   113  	}
   114  
   115  	// is no-privs/keep-privs set on command line
   116  	if e.EngineConfig.GetNoPrivs() {
   117  		sylog.Debugf("--no-privs requested, no new privileges enabled")
   118  		defaultCapabilities = "no"
   119  	} else if e.EngineConfig.GetKeepPrivs() {
   120  		sylog.Debugf("--keep-privs requested")
   121  		defaultCapabilities = "full"
   122  	}
   123  
   124  	sylog.Debugf("Root %s capabilities", defaultCapabilities)
   125  
   126  	// set default capabilities based on configuration file directive
   127  	switch defaultCapabilities {
   128  	case "full":
   129  		e.EngineConfig.OciConfig.SetupPrivileged(true)
   130  		commonCaps = e.EngineConfig.OciConfig.Process.Capabilities.Permitted
   131  	case "file":
   132  		file, err := os.OpenFile(buildcfg.CAPABILITY_FILE, os.O_RDONLY, 0644)
   133  		if err != nil {
   134  			return fmt.Errorf("while opening capability config file: %s", err)
   135  		}
   136  		defer file.Close()
   137  
   138  		capConfig, err := capabilities.ReadFrom(file)
   139  		if err != nil {
   140  			return fmt.Errorf("while parsing capability config data: %s", err)
   141  		}
   142  
   143  		commonCaps = append(commonCaps, capConfig.ListUserCaps("root")...)
   144  		groups, err := os.Getgroups()
   145  		for _, g := range groups {
   146  			gr, err := user.GetGrGID(uint32(g))
   147  			if err != nil {
   148  				sylog.Debugf("Ignoring group %d: %s", g, err)
   149  				continue
   150  			}
   151  			caps := capConfig.ListGroupCaps(gr.Name)
   152  			commonCaps = append(commonCaps, caps...)
   153  			sylog.Debugf("%s group capabilities %v added", gr.Name, caps)
   154  		}
   155  	default:
   156  		e.EngineConfig.OciConfig.SetProcessNoNewPrivileges(true)
   157  	}
   158  
   159  	caps, _ := capabilities.Split(e.EngineConfig.GetAddCaps())
   160  	for _, cap := range caps {
   161  		found := false
   162  		for _, c := range commonCaps {
   163  			if c == cap {
   164  				found = true
   165  				break
   166  			}
   167  		}
   168  		if !found {
   169  			sylog.Debugf("Root capability %s added", cap)
   170  			commonCaps = append(commonCaps, cap)
   171  		}
   172  	}
   173  
   174  	commonCaps = capabilities.RemoveDuplicated(commonCaps)
   175  
   176  	caps, _ = capabilities.Split(e.EngineConfig.GetDropCaps())
   177  	for _, cap := range caps {
   178  		for i, c := range commonCaps {
   179  			if c == cap {
   180  				sylog.Debugf("Root capability %s dropped", cap)
   181  				commonCaps = append(commonCaps[:i], commonCaps[i+1:]...)
   182  				break
   183  			}
   184  		}
   185  	}
   186  
   187  	e.EngineConfig.OciConfig.Process.Capabilities.Permitted = commonCaps
   188  	e.EngineConfig.OciConfig.Process.Capabilities.Effective = commonCaps
   189  	e.EngineConfig.OciConfig.Process.Capabilities.Inheritable = commonCaps
   190  	e.EngineConfig.OciConfig.Process.Capabilities.Bounding = commonCaps
   191  	e.EngineConfig.OciConfig.Process.Capabilities.Ambient = commonCaps
   192  
   193  	return nil
   194  }
   195  
   196  func (e *EngineOperations) prepareFd() {
   197  	fds := make([]int, 0)
   198  
   199  	if e.EngineConfig.File.UserBindControl {
   200  		for _, b := range e.EngineConfig.GetBindPath() {
   201  			splitted := strings.Split(b, ":")
   202  
   203  			src, err := filepath.Abs(splitted[0])
   204  			if err != nil {
   205  				continue
   206  			}
   207  
   208  			if !fs.IsDir(src) {
   209  				continue
   210  			}
   211  
   212  			sylog.Debugf("Open file descriptor for %s", src)
   213  			f, err := os.Open(src)
   214  			if err != nil {
   215  				continue
   216  			}
   217  			fds = append(fds, int(f.Fd()))
   218  		}
   219  	}
   220  
   221  	if !e.EngineConfig.GetContain() {
   222  		for _, bindpath := range e.EngineConfig.File.BindPath {
   223  			splitted := strings.Split(bindpath, ":")
   224  			src := splitted[0]
   225  
   226  			if !fs.IsDir(src) {
   227  				continue
   228  			}
   229  
   230  			sylog.Debugf("Open file descriptor for %s", src)
   231  			f, err := os.Open(src)
   232  			if err != nil {
   233  				continue
   234  			}
   235  			fds = append(fds, int(f.Fd()))
   236  		}
   237  	}
   238  
   239  	for _, path := range e.EngineConfig.File.AutofsBugPath {
   240  		if !fs.IsDir(path) {
   241  			continue
   242  		}
   243  
   244  		sylog.Debugf("Open file descriptor for %s", path)
   245  		f, err := os.Open(path)
   246  		if err != nil {
   247  			continue
   248  		}
   249  		fds = append(fds, int(f.Fd()))
   250  	}
   251  
   252  	e.EngineConfig.SetOpenFd(fds)
   253  }
   254  
   255  // prepareContainerConfig is responsible for getting and applying user supplied
   256  // configuration for container creation
   257  func (e *EngineOperations) prepareContainerConfig(starterConfig *starter.Config) error {
   258  	// always set mount namespace
   259  	e.EngineConfig.OciConfig.AddOrReplaceLinuxNamespace(specs.MountNamespace, "")
   260  
   261  	// if PID namespace is not allowed remove it from namespaces
   262  	if !e.EngineConfig.File.AllowPidNs && e.EngineConfig.OciConfig.Linux != nil {
   263  		namespaces := e.EngineConfig.OciConfig.Linux.Namespaces
   264  		for i, ns := range namespaces {
   265  			if ns.Type == specs.PIDNamespace {
   266  				sylog.Debugf("Not virtualizing PID namespace by configuration")
   267  				e.EngineConfig.OciConfig.Linux.Namespaces = append(namespaces[:i], namespaces[i+1:]...)
   268  				break
   269  			}
   270  		}
   271  	}
   272  
   273  	if os.Getuid() == 0 {
   274  		if err := e.prepareRootCaps(); err != nil {
   275  			return err
   276  		}
   277  	} else {
   278  		if err := e.prepareUserCaps(); err != nil {
   279  			return err
   280  		}
   281  	}
   282  
   283  	if e.EngineConfig.File.MountSlave {
   284  		starterConfig.SetMountPropagation("rslave")
   285  	} else {
   286  		starterConfig.SetMountPropagation("rprivate")
   287  	}
   288  
   289  	starterConfig.SetBringLoopbackInterface(true)
   290  
   291  	starterConfig.SetInstance(e.EngineConfig.GetInstance())
   292  
   293  	starterConfig.SetNsFlagsFromSpec(e.EngineConfig.OciConfig.Linux.Namespaces)
   294  
   295  	// user namespace ID mappings
   296  	if e.EngineConfig.OciConfig.Linux != nil {
   297  		if err := starterConfig.AddUIDMappings(e.EngineConfig.OciConfig.Linux.UIDMappings); err != nil {
   298  			return err
   299  		}
   300  		if err := starterConfig.AddGIDMappings(e.EngineConfig.OciConfig.Linux.GIDMappings); err != nil {
   301  			return err
   302  		}
   303  	}
   304  
   305  	param := security.GetParam(e.EngineConfig.GetSecurity(), "selinux")
   306  	if param != "" {
   307  		sylog.Debugf("Applying SELinux context %s", param)
   308  		e.EngineConfig.OciConfig.SetProcessSelinuxLabel(param)
   309  	}
   310  	param = security.GetParam(e.EngineConfig.GetSecurity(), "apparmor")
   311  	if param != "" {
   312  		sylog.Debugf("Applying Apparmor profile %s", param)
   313  		e.EngineConfig.OciConfig.SetProcessApparmorProfile(param)
   314  	}
   315  	param = security.GetParam(e.EngineConfig.GetSecurity(), "seccomp")
   316  	if param != "" {
   317  		sylog.Debugf("Applying seccomp rule from %s", param)
   318  		generator := &e.EngineConfig.OciConfig.Generator
   319  		if err := seccomp.LoadProfileFromFile(param, generator); err != nil {
   320  			return err
   321  		}
   322  	}
   323  
   324  	// open file descriptors (autofs bug path)
   325  	e.prepareFd()
   326  
   327  	return nil
   328  }
   329  
   330  // prepareInstanceJoinConfig is responsible for getting and applying configuration
   331  // to join a running instance
   332  func (e *EngineOperations) prepareInstanceJoinConfig(starterConfig *starter.Config) error {
   333  	name := instance.ExtractName(e.EngineConfig.GetImage())
   334  	file, err := instance.Get(name, instance.SingSubDir)
   335  	if err != nil {
   336  		return err
   337  	}
   338  
   339  	// check if SUID workflow is really used with a privileged instance
   340  	if !file.PrivilegedPath() && starterConfig.GetIsSUID() {
   341  		return fmt.Errorf("try to join unprivileged instance with SUID workflow")
   342  	}
   343  
   344  	instanceEngineConfig := singularityConfig.NewConfig()
   345  
   346  	// extract configuration from instance file
   347  	instanceConfig := &config.Common{
   348  		EngineConfig: instanceEngineConfig,
   349  	}
   350  	if err := json.Unmarshal(file.Config, instanceConfig); err != nil {
   351  		return err
   352  	}
   353  
   354  	starterConfig.SetJoinMount(true)
   355  
   356  	// set namespaces to join
   357  	if err := file.UpdateNamespacesPath(instanceEngineConfig.OciConfig.Linux.Namespaces); err != nil {
   358  		return err
   359  	}
   360  
   361  	if err := starterConfig.SetNsPathFromSpec(instanceEngineConfig.OciConfig.Linux.Namespaces); err != nil {
   362  		return err
   363  	}
   364  
   365  	if e.EngineConfig.OciConfig.Process == nil {
   366  		e.EngineConfig.OciConfig.Process = &specs.Process{}
   367  	}
   368  	if e.EngineConfig.OciConfig.Process.Capabilities == nil {
   369  		e.EngineConfig.OciConfig.Process.Capabilities = &specs.LinuxCapabilities{}
   370  	}
   371  
   372  	// duplicate instance capabilities
   373  	if instanceEngineConfig.OciConfig.Process != nil && instanceEngineConfig.OciConfig.Process.Capabilities != nil {
   374  		e.EngineConfig.OciConfig.Process.Capabilities.Permitted = instanceEngineConfig.OciConfig.Process.Capabilities.Permitted
   375  		e.EngineConfig.OciConfig.Process.Capabilities.Effective = instanceEngineConfig.OciConfig.Process.Capabilities.Effective
   376  		e.EngineConfig.OciConfig.Process.Capabilities.Inheritable = instanceEngineConfig.OciConfig.Process.Capabilities.Inheritable
   377  		e.EngineConfig.OciConfig.Process.Capabilities.Bounding = instanceEngineConfig.OciConfig.Process.Capabilities.Bounding
   378  		e.EngineConfig.OciConfig.Process.Capabilities.Ambient = instanceEngineConfig.OciConfig.Process.Capabilities.Ambient
   379  	}
   380  
   381  	if os.Getuid() == 0 {
   382  		if err := e.prepareRootCaps(); err != nil {
   383  			return err
   384  		}
   385  	} else {
   386  		if err := e.prepareUserCaps(); err != nil {
   387  			return err
   388  		}
   389  	}
   390  
   391  	// restore apparmor profile
   392  	param := security.GetParam(e.EngineConfig.GetSecurity(), "apparmor")
   393  	if param != "" {
   394  		sylog.Debugf("Applying Apparmor profile %s", param)
   395  		e.EngineConfig.OciConfig.SetProcessApparmorProfile(param)
   396  	} else {
   397  		e.EngineConfig.OciConfig.SetProcessApparmorProfile(instanceEngineConfig.OciConfig.Process.ApparmorProfile)
   398  	}
   399  
   400  	// restore selinux context
   401  	param = security.GetParam(e.EngineConfig.GetSecurity(), "selinux")
   402  	if param != "" {
   403  		sylog.Debugf("Applying SELinux context %s", param)
   404  		e.EngineConfig.OciConfig.SetProcessSelinuxLabel(param)
   405  	} else {
   406  		e.EngineConfig.OciConfig.SetProcessSelinuxLabel(instanceEngineConfig.OciConfig.Process.SelinuxLabel)
   407  	}
   408  
   409  	// restore security features
   410  	param = security.GetParam(e.EngineConfig.GetSecurity(), "seccomp")
   411  	if param != "" {
   412  		sylog.Debugf("Applying seccomp rule from %s", param)
   413  		generator := &e.EngineConfig.OciConfig.Generator
   414  		if err := seccomp.LoadProfileFromFile(param, generator); err != nil {
   415  			return err
   416  		}
   417  	} else {
   418  		if instanceEngineConfig.OciConfig.Linux != nil {
   419  			if e.EngineConfig.OciConfig.Linux == nil {
   420  				e.EngineConfig.OciConfig.Linux = &specs.Linux{}
   421  			}
   422  			e.EngineConfig.OciConfig.Linux.Seccomp = instanceEngineConfig.OciConfig.Linux.Seccomp
   423  		}
   424  	}
   425  
   426  	e.EngineConfig.OciConfig.Process.NoNewPrivileges = instanceEngineConfig.OciConfig.Process.NoNewPrivileges
   427  
   428  	return nil
   429  }
   430  
   431  // PrepareConfig checks and prepares the runtime engine config
   432  func (e *EngineOperations) PrepareConfig(starterConfig *starter.Config) error {
   433  	if e.CommonConfig.EngineName != singularityConfig.Name {
   434  		return fmt.Errorf("incorrect engine")
   435  	}
   436  
   437  	configurationFile := buildcfg.SYSCONFDIR + "/singularity/singularity.conf"
   438  	if err := config.Parser(configurationFile, e.EngineConfig.File); err != nil {
   439  		return fmt.Errorf("Unable to parse singularity.conf file: %s", err)
   440  	}
   441  
   442  	if !e.EngineConfig.File.AllowSetuid && starterConfig.GetIsSUID() {
   443  		return fmt.Errorf("SUID workflow disabled by administrator")
   444  	}
   445  
   446  	if starterConfig.GetIsSUID() {
   447  		// check for ownership of singularity.conf
   448  		if !fs.IsOwner(configurationFile, 0) {
   449  			return fmt.Errorf("%s must be owned by root", configurationFile)
   450  		}
   451  		// check for ownership of capability.json
   452  		if !fs.IsOwner(buildcfg.CAPABILITY_FILE, 0) {
   453  			return fmt.Errorf("%s must be owned by root", buildcfg.CAPABILITY_FILE)
   454  		}
   455  		// check for ownership of ecl.toml
   456  		if !fs.IsOwner(buildcfg.ECL_FILE, 0) {
   457  			return fmt.Errorf("%s must be owned by root", buildcfg.ECL_FILE)
   458  		}
   459  	}
   460  
   461  	// Save the current working directory to restore it in stage 2
   462  	// for relative bind paths
   463  	if pwd, err := os.Getwd(); err == nil {
   464  		e.EngineConfig.SetCwd(pwd)
   465  	} else {
   466  		sylog.Warningf("can't determine current working directory")
   467  		e.EngineConfig.SetCwd("/")
   468  	}
   469  
   470  	if e.EngineConfig.OciConfig.Process == nil {
   471  		e.EngineConfig.OciConfig.Process = &specs.Process{}
   472  	}
   473  	if e.EngineConfig.OciConfig.Process.Capabilities == nil {
   474  		e.EngineConfig.OciConfig.Process.Capabilities = &specs.LinuxCapabilities{}
   475  	}
   476  
   477  	uid := e.EngineConfig.GetTargetUID()
   478  	gids := e.EngineConfig.GetTargetGID()
   479  
   480  	if os.Getuid() == 0 && (uid != 0 || len(gids) > 0) {
   481  		starterConfig.SetTargetUID(uid)
   482  		starterConfig.SetTargetGID(gids)
   483  		e.EngineConfig.OciConfig.SetProcessNoNewPrivileges(true)
   484  	}
   485  
   486  	if e.EngineConfig.GetInstanceJoin() {
   487  		if err := e.prepareInstanceJoinConfig(starterConfig); err != nil {
   488  			return err
   489  		}
   490  	} else {
   491  		if err := e.prepareContainerConfig(starterConfig); err != nil {
   492  			return err
   493  		}
   494  		if err := e.loadImages(); err != nil {
   495  			return err
   496  		}
   497  	}
   498  
   499  	starterConfig.SetSharedMount(true)
   500  	starterConfig.SetNoNewPrivs(e.EngineConfig.OciConfig.Process.NoNewPrivileges)
   501  
   502  	if e.EngineConfig.OciConfig.Process != nil && e.EngineConfig.OciConfig.Process.Capabilities != nil {
   503  		starterConfig.SetCapabilities(capabilities.Permitted, e.EngineConfig.OciConfig.Process.Capabilities.Permitted)
   504  		starterConfig.SetCapabilities(capabilities.Effective, e.EngineConfig.OciConfig.Process.Capabilities.Effective)
   505  		starterConfig.SetCapabilities(capabilities.Inheritable, e.EngineConfig.OciConfig.Process.Capabilities.Inheritable)
   506  		starterConfig.SetCapabilities(capabilities.Bounding, e.EngineConfig.OciConfig.Process.Capabilities.Bounding)
   507  		starterConfig.SetCapabilities(capabilities.Ambient, e.EngineConfig.OciConfig.Process.Capabilities.Ambient)
   508  	}
   509  
   510  	return nil
   511  }
   512  
   513  func (e *EngineOperations) loadImages() error {
   514  	images := make([]image.Image, 0)
   515  
   516  	// load rootfs image
   517  	writable := e.EngineConfig.GetWritableImage()
   518  	img, err := e.loadImage(e.EngineConfig.GetImage(), writable)
   519  	if err != nil {
   520  		return err
   521  	}
   522  
   523  	if writable && !img.Writable {
   524  		sylog.Warningf("Can't set writable flag on image, no write permissions")
   525  		e.EngineConfig.SetWritableImage(false)
   526  	}
   527  
   528  	// sandbox are handled differently for security reasons
   529  	if img.Type == image.SANDBOX {
   530  		if img.Path == "/" {
   531  			return fmt.Errorf("/ as sandbox is not authorized")
   532  		}
   533  		if err := mainthread.Chdir(img.Source); err != nil {
   534  			return err
   535  		}
   536  		cwd, err := os.Getwd()
   537  		if err != nil {
   538  			return fmt.Errorf("failed to determine current working directory: %s", err)
   539  		}
   540  		if cwd != img.Path {
   541  			return fmt.Errorf("path mismatch for sandbox %s != %s", cwd, img.Path)
   542  		}
   543  	}
   544  	if img.Type == image.SIF {
   545  		// query the ECL module, proceed if an ecl config file is found
   546  		ecl, err := syecl.LoadConfig(buildcfg.ECL_FILE)
   547  		if err == nil {
   548  			if err = ecl.ValidateConfig(); err != nil {
   549  				return err
   550  			}
   551  			_, err := ecl.ShouldRunFp(img.File)
   552  			if err != nil {
   553  				return err
   554  			}
   555  		}
   556  	}
   557  	// first image is always the root filesystem
   558  	images = append(images, *img)
   559  
   560  	// load overlay images
   561  	for _, overlayImg := range e.EngineConfig.GetOverlayImage() {
   562  		writable := true
   563  
   564  		splitted := strings.SplitN(overlayImg, ":", 2)
   565  		if len(splitted) == 2 {
   566  			if splitted[1] == "ro" {
   567  				writable = false
   568  			}
   569  		}
   570  
   571  		img, err := e.loadImage(splitted[0], writable)
   572  		if err != nil {
   573  			return fmt.Errorf("failed to open overlay image %s: %s", splitted[0], err)
   574  		}
   575  		images = append(images, *img)
   576  	}
   577  
   578  	e.EngineConfig.SetImageList(images)
   579  
   580  	return nil
   581  }
   582  
   583  func (e *EngineOperations) loadImage(path string, writable bool) (*image.Image, error) {
   584  	imgObject, err := image.Init(path, writable)
   585  	if err != nil {
   586  		return nil, err
   587  	}
   588  
   589  	link, err := mainthread.Readlink(imgObject.Source)
   590  	if link != imgObject.Path {
   591  		return nil, fmt.Errorf("resolved path %s doesn't match with opened path %s", imgObject.Path, link)
   592  	}
   593  
   594  	if len(e.EngineConfig.File.LimitContainerPaths) != 0 {
   595  		if authorized, err := imgObject.AuthorizedPath(e.EngineConfig.File.LimitContainerPaths); err != nil {
   596  			return nil, err
   597  		} else if !authorized {
   598  			return nil, fmt.Errorf("Singularity image is not in an allowed configured path")
   599  		}
   600  	}
   601  	if len(e.EngineConfig.File.LimitContainerGroups) != 0 {
   602  		if authorized, err := imgObject.AuthorizedGroup(e.EngineConfig.File.LimitContainerGroups); err != nil {
   603  			return nil, err
   604  		} else if !authorized {
   605  			return nil, fmt.Errorf("Singularity image is not owned by required group(s)")
   606  		}
   607  	}
   608  	if len(e.EngineConfig.File.LimitContainerOwners) != 0 {
   609  		if authorized, err := imgObject.AuthorizedOwner(e.EngineConfig.File.LimitContainerOwners); err != nil {
   610  			return nil, err
   611  		} else if !authorized {
   612  			return nil, fmt.Errorf("Singularity image is not owned by required user(s)")
   613  		}
   614  	}
   615  
   616  	switch imgObject.Type {
   617  	case image.SANDBOX:
   618  		if !e.EngineConfig.File.AllowContainerDir {
   619  			return nil, fmt.Errorf("configuration disallows users from running sandbox based containers")
   620  		}
   621  	case image.EXT3:
   622  		if !e.EngineConfig.File.AllowContainerExtfs {
   623  			return nil, fmt.Errorf("configuration disallows users from running extFS based containers")
   624  		}
   625  	case image.SQUASHFS:
   626  		if !e.EngineConfig.File.AllowContainerSquashfs {
   627  			return nil, fmt.Errorf("configuration disallows users from running squashFS based containers")
   628  		}
   629  	}
   630  	return imgObject, nil
   631  }