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 }