Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sudo-less operation #2408

Merged
merged 24 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2f70d6f
containerlab: Add sudoless operation.
vista- Jan 17, 2025
3a054de
containerlab: Add sudoless changes to packaging+install script
vista- Jan 17, 2025
cacfbe4
containerlab: Add missing root privilege gain to disable-tx-offload c…
vista- Jan 17, 2025
96bd0d6
containerlab: clab_admins should be added as as system group
vista- Jan 17, 2025
fae3857
containerlab: Change not in user group hint to user usermod instead o…
vista- Jan 17, 2025
f7b392d
containerlab: Add shorthands for root UID and no-modify flags for rea…
vista- Jan 17, 2025
ae42cc1
upgrade: Fix sudoless upgrade
vista- Jan 24, 2025
6f8a796
containerlab: Only create clab_admins group during first upgrade/install
vista- Jan 24, 2025
698e5cc
docs: Add documentation about sudoless operation
vista- Jan 24, 2025
4c99b18
cmd: Fix broken rebase
vista- Jan 27, 2025
a11feee
docs: Fix minimum version for sudoless operations support in install …
vista- Jan 27, 2025
7d307da
cmd: Allow unprivileged users to exec if they are part of the docker …
vista- Jan 27, 2025
94a5ebe
cmd/netem: Add root requirement for show link impairments command
vista- Jan 29, 2025
4da0fe2
format
hellt Jan 29, 2025
fc3906c
docs polish
hellt Jan 29, 2025
2845390
remove href from the embedded code block
hellt Jan 29, 2025
10bd711
cicd, tests: Make tests run sudoless
vista- Jan 30, 2025
6c6d89a
cmd/generate: Get root privileges for deploy action
vista- Jan 30, 2025
d81ed0f
utils/file: Create files as running user instead of effective user
vista- Jan 30, 2025
b6d9fd5
cmd: Only run sudoless if Docker runtime is used
vista- Jan 30, 2025
96b11de
runtimes: Add connectivity check for runtimes
vista- Jan 30, 2025
003fb59
cmd: Don't allow non-Docker runtimes to run as root without membershi…
vista- Jan 30, 2025
8d43626
docs: Add note about non-privileged operations only being supported w…
vista- Jan 30, 2025
442322c
mocks: Update container runtime mock
vista- Jan 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ jobs:
with:
name: containerlab
- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PY_VER }}
Expand Down Expand Up @@ -226,7 +226,7 @@ jobs:
with:
name: containerlab
- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PY_VER }}
Expand Down Expand Up @@ -289,7 +289,7 @@ jobs:
with:
name: containerlab
- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PY_VER }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cisco_iol-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
name: containerlab

- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab

- uses: actions/setup-python@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/fortigate-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
name: containerlab

- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab

- uses: actions/setup-python@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/kind-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
name: containerlab

- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab

- uses: actions/setup-python@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
name: containerlab

- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab

- name: Setup Podman
if: matrix.runtime == 'podman'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/srlinux-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
name: containerlab

- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab

- name: Setup Podman
if: matrix.runtime == 'podman'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sros-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
name: containerlab

- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab

- uses: actions/setup-python@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/vxlan-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
name: containerlab

- name: Move containerlab to usr/bin
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chmod a+x /usr/bin/containerlab
run: sudo mv ./containerlab /usr/bin/containerlab && sudo chown root:root /usr/bin/containerlab && sudo chmod 4755 /usr/bin/containerlab

- uses: actions/setup-python@v5
with:
Expand Down
4 changes: 4 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ nfpms:
postinstall: ./utils/postinstall.sh
bindir: /usr/bin
contents:
- src: ./containerlab
dst: /usr/bin/containerlab
file_info:
mode: 4755 # SUID bit set
- src: ./lab-examples
dst: /etc/containerlab/lab-examples
- src: /usr/bin/containerlab
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ ifndef suite
override suite = .
endif
robot-test: build-with-podman-debug
sudo chown root:root $(BINARY) && sudo chmod 4755 $(BINARY)
CLAB_BIN=$(BINARY) $$PWD/tests/rf-run.sh $(runtime) $$PWD/tests/$(suite)

MOCKDIR = ./mocks
Expand Down
12 changes: 12 additions & 0 deletions clab/clab.go
Original file line number Diff line number Diff line change
Expand Up @@ -1297,3 +1297,15 @@ func (c *CLab) Exec(ctx context.Context, cmds []string, options *ExecOptions) (*

return resultCollection, nil
}

// CheckConnectivity checks the connectivity to all container runtimes, returns an error if it encounters any, otherwise nil.
func (c *CLab) CheckConnectivity(ctx context.Context) error {
for _, r := range c.Runtimes {
err := r.CheckConnection(ctx)
if err != nil {
return fmt.Errorf("could not connect to container runtime: %v", err)
}
}

return nil
}
94 changes: 89 additions & 5 deletions cmd/common/sudo.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,100 @@
package common

import (
"errors"
"fmt"
"os"
"os/user"
"slices"

"github.com/spf13/cobra"
"golang.org/x/sys/unix"

log "github.com/sirupsen/logrus"
)

const (
CLAB_AUTHORISED_GROUP = "clab_admins"
ROOT_UID = 0
NOMODIFY = -1
)

func SudoCheck(_ *cobra.Command, _ []string) error {
id := os.Geteuid()
if id != 0 {
return errors.New("containerlab requires sudo privileges to run")
func CheckAndGetRootPrivs(_ *cobra.Command, _ []string) error {
_, euid, suid := unix.Getresuid()
if euid != 0 && suid != 0 {
return fmt.Errorf("this containerlab command requires root privileges or root via SUID to run, effective UID: %v SUID: %v", euid, suid)
}

if euid != 0 && suid == 0 {
clabGroupExists := true
clabGroup, err := user.LookupGroup(CLAB_AUTHORISED_GROUP)
if err != nil {
if _, ok := err.(user.UnknownGroupError); ok {
log.Debug("Containerlab admin group does not exist, skipping group membership check")
clabGroupExists = false
} else {
return fmt.Errorf("failed to lookup containerlab admin group: %v", err)
}
}

if clabGroupExists {
currentEffUser, err := user.Current()
if err != nil {
return err
}

effUserGroupIDs, err := currentEffUser.GroupIds()
if err != nil {
return err
}

if !slices.Contains(effUserGroupIDs, clabGroup.Gid) {
return fmt.Errorf("user '%v' is not part of containerlab admin group 'clab_admins' (GID %v), which is required to execute this command.\nTo add yourself to this group, run the following command:\n\t$ sudo gpasswd -a %v clab_admins",
currentEffUser.Username, clabGroup.Gid, currentEffUser.Username)
}

log.Debug("Group membership check passed")
}

err = obtainRootPrivs()
if err != nil {
return err
}
}

return nil
}

func obtainRootPrivs() error {
// Escalate to root privileges, changing saved UIDs to root/current group to be able to retain privilege escalation
err := changePrivileges(0, os.Getgid(), 0, os.Getgid())
if err != nil {
return err
}

log.Debug("Obtained root privileges")

return nil
}

func DropRootPrivs() error {
// Drop privileges to the running user, retaining current saved IDs
err := changePrivileges(os.Getuid(), os.Getgid(), -1, -1)
if err != nil {
return err
}

log.Debug("Dropped root privileges")

return nil
}

func changePrivileges(new_uid, new_gid, saved_uid, saved_gid int) error {
if err := unix.Setresuid(-1, new_uid, saved_uid); err != nil {
return fmt.Errorf("failed to set UID: %v", err)
}
if err := unix.Setresgid(-1, new_gid, saved_gid); err != nil {
return fmt.Errorf("failed to set GID: %v", err)
}
log.Debugf("Changed running UIDs to UID: %d GID: %d", new_uid, new_gid)
return nil
}
2 changes: 1 addition & 1 deletion cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var deployCmd = &cobra.Command{
Long: "deploy a lab based defined by means of the topology definition file\nreference: https://containerlab.dev/cmd/deploy/",
Aliases: []string{"dep"},
SilenceUsage: true,
PreRunE: common.SudoCheck,
PreRunE: common.CheckAndGetRootPrivs,
RunE: deployFn,
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var destroyCmd = &cobra.Command{
Short: "destroy a lab",
Long: "destroy a lab based defined by means of the topology definition file\nreference: https://containerlab.dev/cmd/destroy/",
Aliases: []string{"des"},
PreRunE: common.SudoCheck,
PreRunE: common.CheckAndGetRootPrivs,
RunE: destroyFn,
}

Expand Down
2 changes: 2 additions & 0 deletions cmd/disableTxOffload.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/srl-labs/containerlab/clab"
"github.com/srl-labs/containerlab/cmd/common"
"github.com/srl-labs/containerlab/runtime"
"github.com/srl-labs/containerlab/utils"
)
Expand All @@ -21,6 +22,7 @@ var disableTxOffloadCmd = &cobra.Command{
Use: "disable-tx-offload",
Short: "disables tx checksum offload on eth0 interface of a container",

PreRunE: common.CheckAndGetRootPrivs,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()

Expand Down
13 changes: 8 additions & 5 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/spf13/cobra"
"github.com/srl-labs/containerlab/clab"
"github.com/srl-labs/containerlab/clab/exec"
"github.com/srl-labs/containerlab/cmd/common"
"github.com/srl-labs/containerlab/labels"
"github.com/srl-labs/containerlab/runtime"
"github.com/srl-labs/containerlab/types"
Expand All @@ -26,10 +25,9 @@ var (

// execCmd represents the exec command.
var execCmd = &cobra.Command{
Use: "exec",
Short: "execute a command on one or multiple containers",
PreRunE: common.SudoCheck,
RunE: execFn,
Use: "exec",
Short: "execute a command on one or multiple containers",
RunE: execFn,
}

func execFn(_ *cobra.Command, _ []string) error {
Expand Down Expand Up @@ -75,6 +73,11 @@ func execFn(_ *cobra.Command, _ []string) error {
return err
}

err = c.CheckConnectivity(ctx)
if err != nil {
return err
}

var filters []*types.GenericFilter

if len(labelsFilter) != 0 {
Expand Down
5 changes: 5 additions & 0 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/srl-labs/containerlab/clab"
"github.com/srl-labs/containerlab/cmd/common"
"github.com/srl-labs/containerlab/links"
"github.com/srl-labs/containerlab/nodes"
"github.com/srl-labs/containerlab/types"
Expand Down Expand Up @@ -89,6 +90,10 @@ var generateCmd = &cobra.Command{
}
}
if deploy {
err = common.CheckAndGetRootPrivs(nil, nil)
if err != nil {
return err
}
reconfigure = true
if file == "" {
file = fmt.Sprintf("%s.clab.yml", name)
Expand Down
7 changes: 5 additions & 2 deletions cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/srl-labs/containerlab/clab"
"github.com/srl-labs/containerlab/cmd/common"
"github.com/srl-labs/containerlab/labels"
"github.com/srl-labs/containerlab/runtime"
"github.com/srl-labs/containerlab/types"
Expand All @@ -38,7 +37,6 @@ var inspectCmd = &cobra.Command{
Short: "inspect lab details",
Long: "show details about a particular lab or all running labs\nreference: https://containerlab.dev/cmd/inspect/",
Aliases: []string{"ins", "i"},
PreRunE: common.SudoCheck,
RunE: inspectFn,
}

Expand Down Expand Up @@ -85,6 +83,11 @@ func inspectFn(_ *cobra.Command, _ []string) error {
return fmt.Errorf("could not parse the topology file: %v", err)
}

err = c.CheckConnectivity(ctx)
if err != nil {
return err
}

var containers []runtime.GenericContainer
var glabels []*types.GenericFilter

Expand Down
2 changes: 1 addition & 1 deletion cmd/redeploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var redeployCmd = &cobra.Command{
Short: "destroy and redeploy a lab",
Long: "destroy a lab and deploy it again based on the topology definition file\nreference: https://containerlab.dev/cmd/redeploy/",
Aliases: []string{"rdep"},
PreRunE: common.SudoCheck,
PreRunE: common.CheckAndGetRootPrivs,
SilenceUsage: true,
RunE: redeployFn,
}
Expand Down
13 changes: 13 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/srl-labs/containerlab/cmd/common"
"github.com/srl-labs/containerlab/cmd/version"
"github.com/srl-labs/containerlab/git"
"github.com/srl-labs/containerlab/utils"
Expand Down Expand Up @@ -89,6 +90,18 @@ func preRunFn(cmd *cobra.Command, _ []string) error {
// setting output to stderr, so that json outputs can be parsed
log.SetOutput(os.Stderr)

err := common.DropRootPrivs()
if err != nil {
return err
}
// Rootless operations only supported for Docker runtime
if rt != "" && rt != "docker" {
err := common.CheckAndGetRootPrivs(cmd, nil)
if err != nil {
return err
}
}

return getTopoFilePath(cmd)
}

Expand Down
2 changes: 0 additions & 2 deletions cmd/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/srl-labs/containerlab/clab"
"github.com/srl-labs/containerlab/cmd/common"
"github.com/srl-labs/containerlab/links"
"github.com/srl-labs/containerlab/nodes"
"github.com/srl-labs/containerlab/runtime"
Expand All @@ -24,7 +23,6 @@ var saveCmd = &cobra.Command{
Short: "save containers configuration",
Long: `save performs a configuration save. The exact command that is used to save the config depends on the node kind.
Refer to the https://containerlab.dev/cmd/save/ documentation to see the exact command used per node's kind`,
PreRunE: common.SudoCheck,
RunE: func(_ *cobra.Command, _ []string) error {
if name == "" && topo == "" {
return fmt.Errorf("provide topology file path with --topo flag")
Expand Down
Loading
Loading