From 52fb2ca23f6bbde0518d7a8fdaad250cb2d1c125 Mon Sep 17 00:00:00 2001 From: Adrien CABARBAYE Date: Thu, 31 Jul 2025 18:33:30 +0100 Subject: [PATCH] :bug: `[platform]` make sure command with privileges are run when passwords are not needed --- .secrets.baseline | 2 +- changes/20250731182237.bugfix | 1 + changes/20250731182404.bugfix | 1 + utils/filesystem/extendedosfs.go | 13 +++++++++++++ utils/filesystem/filesystem.go | 2 +- utils/platform/cmd.go | 2 +- utils/platform/cmd_posix.go | 11 ++++++++++- utils/platform/cmd_windows.go | 8 +++++++- utils/subprocess/command/cmd.go | 4 ++-- 9 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 changes/20250731182237.bugfix create mode 100644 changes/20250731182404.bugfix diff --git a/.secrets.baseline b/.secrets.baseline index eaa6d7fe64..2dc4e85872 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -272,5 +272,5 @@ } ] }, - "generated_at": "2025-07-09T20:27:15Z" + "generated_at": "2025-07-31T17:26:46Z" } diff --git a/changes/20250731182237.bugfix b/changes/20250731182237.bugfix new file mode 100644 index 0000000000..126e4be653 --- /dev/null +++ b/changes/20250731182237.bugfix @@ -0,0 +1 @@ +:bug: `[platform]` make sure command with privileges are run when passwords are not needed diff --git a/changes/20250731182404.bugfix b/changes/20250731182404.bugfix new file mode 100644 index 0000000000..27918c7a09 --- /dev/null +++ b/changes/20250731182404.bugfix @@ -0,0 +1 @@ +:bug: `[filesystem]` make sure to unlink files when removal is called so that sockets are correctly closed diff --git a/utils/filesystem/extendedosfs.go b/utils/filesystem/extendedosfs.go index 81b7341da1..ba01768311 100644 --- a/utils/filesystem/extendedosfs.go +++ b/utils/filesystem/extendedosfs.go @@ -8,9 +8,11 @@ package filesystem import ( "context" "os" + "syscall" "github.com/spf13/afero" + "github.com/ARM-software/golang-utils/utils/commonerrors" "github.com/ARM-software/golang-utils/utils/platform" ) @@ -19,6 +21,17 @@ type ExtendedOsFs struct { afero.OsFs } +func (c *ExtendedOsFs) Remove(name string) (err error) { + err = commonerrors.Ignore(ConvertFileSystemError(c.OsFs.Remove(name)), commonerrors.ErrNotFound) + if err != nil { + return + } + // The following is to ensure sockets are correctly removed + // https://stackoverflow.com/questions/16681944/how-to-reliably-unlink-a-unix-domain-socket-in-go-programming-language + err = commonerrors.Ignore(ConvertFileSystemError(syscall.Unlink(name)), commonerrors.ErrNotFound) + return +} + func (c *ExtendedOsFs) ChownIfPossible(name string, uid int, gid int) error { return ConvertFileSystemError(c.OsFs.Chown(name, uid, gid)) } diff --git a/utils/filesystem/filesystem.go b/utils/filesystem/filesystem.go index 5435cb89c3..8043901be7 100644 --- a/utils/filesystem/filesystem.go +++ b/utils/filesystem/filesystem.go @@ -115,7 +115,7 @@ func ConvertFileSystemError(err error) error { return commonerrors.WrapError(commonerrors.ErrConflict, err, "") case commonerrors.CorrespondTo(err, "required privilege is not held") || commonerrors.CorrespondTo(err, "operation not permitted"): return commonerrors.WrapError(commonerrors.ErrForbidden, err, "") - case os.IsNotExist(err) || commonerrors.Any(err, os.ErrNotExist, afero.ErrFileNotFound): + case os.IsNotExist(err) || commonerrors.Any(err, os.ErrNotExist, afero.ErrFileNotFound) || IsPathNotExist(err) || commonerrors.CorrespondTo(err, "No such file or directory"): return commonerrors.WrapError(commonerrors.ErrNotFound, err, "") case commonerrors.Any(err, os.ErrNoDeadline): return commonerrors.WrapError(commonerrors.ErrUnsupported, err, "file type does not support deadline") diff --git a/utils/platform/cmd.go b/utils/platform/cmd.go index f6132ecfeb..0f5bbb0774 100644 --- a/utils/platform/cmd.go +++ b/utils/platform/cmd.go @@ -15,7 +15,7 @@ func WithPrivileges(cmd *command.CommandAsDifferentUser) (cmdWithPrivileges *com if err != nil { return } - if !hasPrivileges { + if !hasPrivileges && hasPasswordlessPrivileges() { cmdWithPrivileges.Prepend(getRunCommandWithPrivileges()) } return diff --git a/utils/platform/cmd_posix.go b/utils/platform/cmd_posix.go index 861cc04fa5..e2716c5020 100644 --- a/utils/platform/cmd_posix.go +++ b/utils/platform/cmd_posix.go @@ -3,7 +3,11 @@ package platform -import "github.com/ARM-software/golang-utils/utils/subprocess/command" +import ( + "os/exec" + + "github.com/ARM-software/golang-utils/utils/subprocess/command" +) var ( // sudoCommand describes the command to use to execute command as root @@ -22,3 +26,8 @@ func DefineSudoCommand(args ...string) { func getRunCommandWithPrivileges() *command.CommandAsDifferentUser { return sudoCommand } + +func hasPasswordlessPrivileges() bool { + // See https://www.baeldung.com/linux/sudo-passwordless-check + return exec.Command("sh", "-c", "sudo -n true 2>/dev/null || exit 1").Run() == nil +} diff --git a/utils/platform/cmd_windows.go b/utils/platform/cmd_windows.go index 7fd7bb7230..7ad7bc721b 100644 --- a/utils/platform/cmd_windows.go +++ b/utils/platform/cmd_windows.go @@ -3,7 +3,9 @@ package platform -import "github.com/ARM-software/golang-utils/utils/subprocess/command" +import ( + "github.com/ARM-software/golang-utils/utils/subprocess/command" +) var ( // runAsAdmin describes the command to use to run command as Administrator @@ -23,3 +25,7 @@ func DefineRunAsAdmin(args ...string) { func getRunCommandWithPrivileges() *command.CommandAsDifferentUser { return runAsAdmin } + +func hasPasswordlessPrivileges() bool { + return true +} diff --git a/utils/subprocess/command/cmd.go b/utils/subprocess/command/cmd.go index 5eb9277e0a..f3e9bebc3c 100644 --- a/utils/subprocess/command/cmd.go +++ b/utils/subprocess/command/cmd.go @@ -6,7 +6,7 @@ import ( "sync" ) -// CommandAsDifferentUser helps redefining commands so that they are run as a different user or with more privileges. +// CommandAsDifferentUser helps to redefine commands so that they are run as a different user or with more privileges. type CommandAsDifferentUser struct { mu sync.RWMutex // changeUserCmd describes the command to use to execute any command as a different user @@ -65,7 +65,7 @@ func (c *CommandAsDifferentUser) Prepend(cmd *CommandAsDifferentUser) *CommandAs return c } -// NewCommandAsDifferentUser defines a command wrapper which helps redefining commands so that they are run as a different user. +// NewCommandAsDifferentUser defines a command wrapper which helps to redefine commands so that they are run as a different user. // e.g. // - switchUserCmd="sudo" to run commands as `root` // - switchUserCmd="su", "tom" if `tom` has enough privileges to run the command