diff --git a/build.go b/build.go index a74117d..dd947cd 100644 --- a/build.go +++ b/build.go @@ -63,7 +63,7 @@ func BuildCmd() *cli.Command { }, }, Action: func(c *cli.Context) error { - if err := utils.EnuseIsPrivilegedGroupMember(); err != nil { + if err := utils.CheckUserPrivileges(); err != nil { return err } diff --git a/internal/utils/cmd.go b/internal/utils/cmd.go index b2c9fe2..90484f2 100644 --- a/internal/utils/cmd.go +++ b/internal/utils/cmd.go @@ -19,7 +19,6 @@ package utils import ( "os" "os/exec" - "os/user" "github.com/leonelquinteros/gotext" "github.com/urfave/cli/v2" @@ -33,40 +32,10 @@ func IsNotRoot() bool { return os.Getuid() != 0 } -// EnuseIsPrivilegedGroupMember проверяет, что пользователь является членом привилегированной группы (wheel) +// EnuseIsPrivilegedGroupMember проверяет, что пользователь является членом привилегированной группы (wheel/sudo) +// DEPRECATED: используйте CheckUserPrivileges() из utils.go func EnuseIsPrivilegedGroupMember() error { - // В CI пропускаем проверку группы wheel - if os.Getenv("CI") == "true" { - return nil - } - - // Если пользователь root, пропускаем проверку - if os.Geteuid() == 0 { - return nil - } - - currentUser, err := user.Current() - if err != nil { - return err - } - - privilegedGroup := GetPrivilegedGroup() - group, err := user.LookupGroup(privilegedGroup) - if err != nil { - return err - } - - groups, err := currentUser.GroupIds() - if err != nil { - return err - } - - for _, gid := range groups { - if gid == group.Gid { - return nil - } - } - return cliutils.FormatCliExit(gotext.Get("You need to be a %s member to perform this action", privilegedGroup), nil) + return CheckUserPrivileges() } func RootNeededAction(f cli.ActionFunc) cli.ActionFunc { diff --git a/internal/utils/utils.go b/internal/utils/utils.go index b62f1ff..f3cf019 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -17,8 +17,10 @@ package utils import ( + "fmt" "os" "os/exec" + "os/user" "strings" "golang.org/x/sys/unix" @@ -28,66 +30,135 @@ func NoNewPrivs() error { return unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) } -// EnsureTempDirWithRootOwner создает каталог в /tmp/alr с правами для привилегированной группы -// Все каталоги в /tmp/alr принадлежат root:привилегированная_группа с правами 775 +// EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы +// Все каталоги в /tmp/alr и /var/cache/alr принадлежат root:привилегированная_группа с правами 2775 // Для других каталогов использует стандартные права func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error { - if strings.HasPrefix(path, "/tmp/alr") { - // Сначала создаем директорию обычным способом - err := os.MkdirAll(path, mode) - if err != nil { - return err - } - + needsElevation := strings.HasPrefix(path, "/tmp/alr") || strings.HasPrefix(path, "/var/cache/alr") + + if needsElevation { // В CI или если мы уже root, не нужно использовать sudo isRoot := os.Geteuid() == 0 isCI := os.Getenv("CI") == "true" - + // В CI создаем директории с обычными правами if isCI { + err := os.MkdirAll(path, mode) + if err != nil { + return err + } // В CI не используем группу wheel и не меняем права // Устанавливаем базовые права 777 для временных каталогов chmodCmd := exec.Command("chmod", "777", path) chmodCmd.Run() // Игнорируем ошибки return nil } - + // Для обычной работы устанавливаем права и привилегированную группу permissions := "2775" group := GetPrivilegedGroup() - - var chmodCmd, chownCmd *exec.Cmd + + var mkdirCmd, chmodCmd, chownCmd *exec.Cmd if isRoot { // Выполняем команды напрямую без sudo + mkdirCmd = exec.Command("mkdir", "-p", path) chmodCmd = exec.Command("chmod", permissions, path) chownCmd = exec.Command("chown", "root:"+group, path) } else { - // Используем sudo для обычных пользователей + // Используем sudo для всех операций с привилегированными каталогами + mkdirCmd = exec.Command("sudo", "mkdir", "-p", path) chmodCmd = exec.Command("sudo", "chmod", permissions, path) chownCmd = exec.Command("sudo", "chown", "root:"+group, path) } - - // Устанавливаем права с setgid битом + + // Создаем директорию через sudo если нужно + err := mkdirCmd.Run() + if err != nil { + // Игнорируем ошибку если директория уже существует + if !isRoot { + // Проверяем существует ли директория + if _, statErr := os.Stat(path); statErr != nil { + return fmt.Errorf("не удалось создать директорию %s: %w", path, err) + } + } + } + + // Устанавливаем права с setgid битом для наследования группы err = chmodCmd.Run() if err != nil { - // Для root игнорируем ошибки, если группа не существует if !isRoot { - return err + return fmt.Errorf("не удалось установить права на %s: %w", path, err) } } - - // Устанавливаем владельца root:wheel + + // Устанавливаем владельца root:группа err = chownCmd.Run() if err != nil { - // Для root игнорируем ошибки, если группа не существует if !isRoot { - return err + return fmt.Errorf("не удалось установить владельца на %s: %w", path, err) } } - + return nil } - + // Для остальных каталогов обычное создание return os.MkdirAll(path, mode) } + +// IsUserInGroup проверяет, состоит ли пользователь в указанной группе +func IsUserInGroup(username, groupname string) bool { + u, err := user.Lookup(username) + if err != nil { + return false + } + + groups, err := u.GroupIds() + if err != nil { + return false + } + + targetGroup, err := user.LookupGroup(groupname) + if err != nil { + return false + } + + for _, gid := range groups { + if gid == targetGroup.Gid { + return true + } + } + return false +} + +// CheckUserPrivileges проверяет, что пользователь имеет необходимые привилегии для работы с ALR +// Пользователь должен быть root или состоять в группе wheel/sudo +func CheckUserPrivileges() error { + // Если пользователь root - все в порядке + if os.Geteuid() == 0 { + return nil + } + + // В CI не проверяем привилегии + if os.Getenv("CI") == "true" { + return nil + } + + currentUser, err := user.Current() + if err != nil { + return fmt.Errorf("не удалось получить информацию о текущем пользователе: %w", err) + } + + privilegedGroup := GetPrivilegedGroup() + + // Проверяем членство в привилегированной группе + if !IsUserInGroup(currentUser.Username, privilegedGroup) { + return fmt.Errorf("пользователь %s не имеет необходимых привилегий для работы с ALR.\n"+ + "Для работы с ALR необходимо быть пользователем root или состоять в группе %s.\n"+ + "Выполните команду: sudo usermod -a -G %s %s\n"+ + "Затем перезайдите в систему или выполните: newgrp %s", + currentUser.Username, privilegedGroup, privilegedGroup, currentUser.Username, privilegedGroup) + } + + return nil +}