Skip to content

Commit

Permalink
fix: CNI: avoid error with iptables setuid check (release-4.2)
Browse files Browse the repository at this point in the history
Pick sylabs#3444

Newer versions of the `iptables` command use a real vs effective uid
check whether they are being called from a setuid script, and exit if
that's the case.

This check was added because iptables can call out to binaries /
libraries on `PATH` / `LD_LIBRARY_PATH`, and these are generally under
control of the user - allowing privilege escalation attackes.

Singularity sanitizes the environment before running CNI plugins, which
will call `iptables`, so we can set both real and effective uid to 0 to
avoid the error.

While we are here, make `PATH` sanitization the default in the network
code, rather than relying on the caller applying it.

Fixes sylabs#3318
  • Loading branch information
dtrudg committed Dec 20, 2024
1 parent 97288f8 commit 22a3c4c
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

- Fix regression from 4.1.5 that overwrites source image runscript, environment
etc. in build from local image.
- Avoid error in CNI network setup with newer versions of iptables that include
a setuid caller check.

## 4.2.1 \[2024-09-13\]

Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/runtime/engine/singularity/cleanup_linux.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2021, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
Expand Down Expand Up @@ -77,7 +77,7 @@ func (e *EngineOperations) CleanupContainer(ctx context.Context, _ error, _ sysc
privileged := false
// If a CNI configuration was allowed as non-root (or fakeroot)
if net != "none" && os.Geteuid() != 0 {
priv.Escalate()
priv.EscalateRealEffective()
privileged = true
}
sylog.Debugf("Cleaning up CNI network config %s", net)
Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/runtime/engine/singularity/container_linux.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
// Copyright (c) Contributors to the Apptainer project, established as
// Apptainer a Series of LF Projects LLC.
// This software is licensed under a 3-clause BSD license. Please consult the
Expand Down Expand Up @@ -2398,7 +2398,7 @@ func (c *container) prepareNetworkSetup(system *mount.System, pid int) (func(con
}
}
if euid != 0 {
priv.Escalate()
priv.EscalateRealEffective()
defer priv.Drop()
}
}
Expand Down
31 changes: 19 additions & 12 deletions internal/pkg/util/priv/priv_linux.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
// Copyright (c) 2018-2022, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package priv

import (
"os"
"runtime"
"syscall"

"github.com/sylabs/singularity/v4/pkg/sylog"
"golang.org/x/sys/unix"
)

// Escalate escalates privileges of the thread or process.
// Since Go 1.16 syscall.Setresuid is an all-thread operation.
// A runtime.LockOSThread operation remains for older versions of Go.
func Escalate() error {
// EscalateRealEffective escalates real and effective uid of the thread or
// process to root (0). The current real uid is set as the saved set-user-ID.
// Since Go 1.16 syscall.Setresuid is an all-thread operation. A
// runtime.LockOSThread operation remains for older versions of Go.
func EscalateRealEffective() error {
runtime.LockOSThread()
uid := os.Getuid()
return syscall.Setresuid(uid, 0, uid)
uid, _, _ := unix.Getresuid()
sylog.Debugf("Escalate r/e/s: %d/%d/%d", 0, 0, uid)
return syscall.Setresuid(0, 0, uid)
}

// Drop drops privileges of the thread or process.
// Since Go 1.16 syscall.Setresuid is an all-thread operation.
// A runtime.LockOSThread operation remains for older versions of Go.
// Drop drops privileges of the thread or process. The real and effective uid
// are set to the value of the saved set-user-ID. The saved set-user-ID is
// returned to 0 to allow escalation in future. Since Go 1.16 syscall.Setresuid
// is an all-thread operation. A runtime.LockOSThread operation remains for
// older versions of Go.
func Drop() error {
defer runtime.UnlockOSThread()
uid := os.Getuid()
_, _, uid := unix.Getresuid()
sylog.Debugf("Drop r/e/s: %d/%d/%d", uid, uid, 0)
return syscall.Setresuid(uid, uid, 0)
}
16 changes: 10 additions & 6 deletions pkg/network/network_linux.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
Expand All @@ -22,6 +22,7 @@ import (
cnitypes "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
"github.com/sylabs/singularity/v4/internal/pkg/util/env"
"github.com/sylabs/singularity/v4/pkg/sylog"
)

type netError string
Expand Down Expand Up @@ -451,12 +452,15 @@ func (m *Setup) DelNetworks(ctx context.Context) error {
}

func (m *Setup) command(ctx context.Context, command string) error {
if m.envPath != "" {
backupEnv := os.Environ()
os.Clearenv()
os.Setenv("PATH", m.envPath)
defer env.SetFromList(backupEnv)
if m.envPath == "" {
sylog.Debugf("Network envPath is unset. Setting PATH to a safe default.")
m.envPath = "/bin:/sbin:/usr/bin:/usr/sbin"
}
sylog.Debugf("Network envPath: %s", m.envPath)
backupEnv := os.Environ()
os.Clearenv()
os.Setenv("PATH", m.envPath)
defer env.SetFromList(backupEnv)

config := &libcni.CNIConfig{Path: []string{m.cniPath.Plugin}}

Expand Down

0 comments on commit 22a3c4c

Please sign in to comment.