Skip to content

Commit 6af15b6

Browse files
authored
Merge pull request #3445 from dtrudg/pick-3444
fix: CNI: avoid error with iptables setuid check (release-4.2)
2 parents 7a7e790 + 2b5f914 commit 6af15b6

File tree

6 files changed

+102
-32
lines changed

6 files changed

+102
-32
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
`~/.singularity` is on a filesystem that does not fully support overlay.
1111
- Add more intuitive error message for rootless `build --oci` when required
1212
`XDG_RUNTIME_DIR` env var is not set.
13+
- Avoid error in CNI network setup with newer versions of iptables that include
14+
a setuid caller check.
1315

1416
### New Features & Functionality
1517

internal/pkg/runtime/engine/singularity/cleanup_linux.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018-2021, Sylabs Inc. All rights reserved.
1+
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
22
// This software is licensed under a 3-clause BSD license. Please consult the
33
// LICENSE.md file distributed with the sources of this project regarding your
44
// rights to use or distribute this software.
@@ -74,18 +74,23 @@ func (e *EngineOperations) CleanupContainer(ctx context.Context, _ error, _ sysc
7474

7575
if networkSetup != nil {
7676
net := e.EngineConfig.GetNetwork()
77-
privileged := false
77+
var dropPrivs priv.DropPrivsFunc
7878
// If a CNI configuration was allowed as non-root (or fakeroot)
7979
if net != "none" && os.Geteuid() != 0 {
80-
priv.Escalate()
81-
privileged = true
80+
var err error
81+
dropPrivs, err = priv.EscalateRealEffective()
82+
if err != nil {
83+
return err
84+
}
8285
}
8386
sylog.Debugf("Cleaning up CNI network config %s", net)
8487
if err := networkSetup.DelNetworks(ctx); err != nil {
8588
sylog.Errorf("could not delete networks: %v", err)
8689
}
87-
if privileged {
88-
priv.Drop()
90+
if dropPrivs != nil {
91+
if err := dropPrivs(); err != nil {
92+
sylog.Fatalf("while dropping privilege: %v", err)
93+
}
8994
}
9095
}
9196

internal/pkg/runtime/engine/singularity/container_linux.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
1+
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
22
// Copyright (c) Contributors to the Apptainer project, established as
33
// Apptainer a Series of LF Projects LLC.
44
// This software is licensed under a 3-clause BSD license. Please consult the
@@ -2398,8 +2398,15 @@ func (c *container) prepareNetworkSetup(system *mount.System, pid int) (func(con
23982398
}
23992399
}
24002400
if euid != 0 {
2401-
priv.Escalate()
2402-
defer priv.Drop()
2401+
dropPrivs, err := priv.EscalateRealEffective()
2402+
if err != nil {
2403+
return err
2404+
}
2405+
defer func() {
2406+
if err := dropPrivs(); err != nil {
2407+
sylog.Fatalf("while dropping privilege: %v", err)
2408+
}
2409+
}()
24032410
}
24042411
}
24052412

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
1-
// Copyright (c) 2018-2022, Sylabs Inc. All rights reserved.
1+
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
22
// This software is licensed under a 3-clause BSD license. Please consult the
33
// LICENSE.md file distributed with the sources of this project regarding your
44
// rights to use or distribute this software.
55

66
package priv
77

88
import (
9-
"os"
109
"runtime"
11-
"syscall"
10+
11+
"github.com/sylabs/singularity/v4/pkg/sylog"
12+
"golang.org/x/sys/unix"
1213
)
1314

14-
// Escalate escalates privileges of the thread or process.
15-
// Since Go 1.16 syscall.Setresuid is an all-thread operation.
16-
// A runtime.LockOSThread operation remains for older versions of Go.
17-
func Escalate() error {
15+
type DropPrivsFunc func() error
16+
17+
// EscalateRealEffective locks the current goroutine to execute on the current
18+
// OS thread, and then escalates the real and effective uid of the current OS
19+
// thread to root (uid 0). The previous real uid is set as the saved
20+
// set-user-ID. A dropPrivsFunc is returned, which must be called to drop
21+
// privileges and unlock the goroutine at the earliest suitable point.
22+
func EscalateRealEffective() (DropPrivsFunc, error) {
1823
runtime.LockOSThread()
19-
uid := os.Getuid()
20-
return syscall.Setresuid(uid, 0, uid)
21-
}
24+
uid, _, _ := unix.Getresuid()
25+
26+
dropPrivsFunc := func() error {
27+
defer runtime.UnlockOSThread()
28+
sylog.Debugf("Drop r/e/s: %d/%d/%d", uid, uid, 0)
29+
return unix.Setresuid(uid, uid, 0)
30+
}
2231

23-
// Drop drops privileges of the thread or process.
24-
// Since Go 1.16 syscall.Setresuid is an all-thread operation.
25-
// A runtime.LockOSThread operation remains for older versions of Go.
26-
func Drop() error {
27-
defer runtime.UnlockOSThread()
28-
uid := os.Getuid()
29-
return syscall.Setresuid(uid, uid, 0)
32+
sylog.Debugf("Escalate r/e/s: %d/%d/%d", 0, 0, uid)
33+
// Note - unix.Setresuid makes a direct syscall which performs a single
34+
// thread escalation. Since Go 1.16, syscall.Setresuid is all-thread.
35+
return dropPrivsFunc, unix.Setresuid(0, 0, uid)
3036
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) 2024, 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 priv
7+
8+
import (
9+
"testing"
10+
11+
"github.com/sylabs/singularity/v4/internal/pkg/test"
12+
"golang.org/x/sys/unix"
13+
)
14+
15+
func TestEscalateRealEffective(t *testing.T) {
16+
test.EnsurePrivilege(t)
17+
test.DropPrivilege(t)
18+
defer test.ResetPrivilege(t)
19+
20+
r, e, s := unix.Getresuid()
21+
if r == 0 || e == 0 {
22+
t.Fatalf("real / effective ID must be non-zero before escalation. Got r/e/s %d/%d/%d", r, e, s)
23+
}
24+
unprivUID := r
25+
26+
drop, err := EscalateRealEffective()
27+
if err != nil {
28+
t.Fatal(err)
29+
}
30+
31+
r, e, s = unix.Getresuid()
32+
t.Logf("Escalated r/e/s: %d/%d/%d", r, e, s)
33+
if r != 0 || e != 0 || s != unprivUID {
34+
t.Fatalf("Expected escalated r/e/s %d/%d/%d, Got r/e/s %d/%d/%d", 0, 0, unprivUID, r, e, s)
35+
}
36+
37+
if err := drop(); err != nil {
38+
t.Fatal(err)
39+
}
40+
41+
r, e, s = unix.Getresuid()
42+
t.Logf("Dropped r/e/s: %d/%d/%d", r, e, s)
43+
if r != unprivUID || e != unprivUID || s != 0 {
44+
t.Fatalf("Expected dropped r/e/s %d/%d/%d, Got r/e/s %d/%d/%d", unprivUID, unprivUID, 0, r, e, s)
45+
}
46+
}

pkg/network/network_linux.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
1+
// Copyright (c) 2018-2024, Sylabs Inc. All rights reserved.
22
// This software is licensed under a 3-clause BSD license. Please consult the
33
// LICENSE.md file distributed with the sources of this project regarding your
44
// rights to use or distribute this software.
@@ -22,6 +22,7 @@ import (
2222
cnitypes "github.com/containernetworking/cni/pkg/types/100"
2323
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
2424
"github.com/sylabs/singularity/v4/internal/pkg/util/env"
25+
"github.com/sylabs/singularity/v4/pkg/sylog"
2526
)
2627

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

453454
func (m *Setup) command(ctx context.Context, command string) error {
454-
if m.envPath != "" {
455-
backupEnv := os.Environ()
456-
os.Clearenv()
457-
os.Setenv("PATH", m.envPath)
458-
defer env.SetFromList(backupEnv)
455+
if m.envPath == "" {
456+
sylog.Debugf("Network envPath is unset. Setting PATH to a safe default.")
457+
m.envPath = "/bin:/sbin:/usr/bin:/usr/sbin"
459458
}
459+
sylog.Debugf("Network envPath: %s", m.envPath)
460+
backupEnv := os.Environ()
461+
os.Clearenv()
462+
os.Setenv("PATH", m.envPath)
463+
defer env.SetFromList(backupEnv)
460464

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

0 commit comments

Comments
 (0)