1 Commits

Author SHA1 Message Date
ef41d682a1 Исправление функционала повышения привилегий
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m12s
Create Release / changelog (push) Successful in 3m8s
2025-09-21 15:04:42 +03:00
3 changed files with 99 additions and 59 deletions

View File

@@ -63,7 +63,7 @@ func BuildCmd() *cli.Command {
}, },
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
if err := utils.EnuseIsPrivilegedGroupMember(); err != nil { if err := utils.CheckUserPrivileges(); err != nil {
return err return err
} }

View File

@@ -19,7 +19,6 @@ package utils
import ( import (
"os" "os"
"os/exec" "os/exec"
"os/user"
"github.com/leonelquinteros/gotext" "github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@@ -33,40 +32,10 @@ func IsNotRoot() bool {
return os.Getuid() != 0 return os.Getuid() != 0
} }
// EnuseIsPrivilegedGroupMember проверяет, что пользователь является членом привилегированной группы (wheel) // EnuseIsPrivilegedGroupMember проверяет, что пользователь является членом привилегированной группы (wheel/sudo)
// DEPRECATED: используйте CheckUserPrivileges() из utils.go
func EnuseIsPrivilegedGroupMember() error { func EnuseIsPrivilegedGroupMember() error {
// В CI пропускаем проверку группы wheel return CheckUserPrivileges()
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)
} }
func RootNeededAction(f cli.ActionFunc) cli.ActionFunc { func RootNeededAction(f cli.ActionFunc) cli.ActionFunc {

View File

@@ -17,8 +17,10 @@
package utils package utils
import ( import (
"fmt"
"os" "os"
"os/exec" "os/exec"
"os/user"
"strings" "strings"
"golang.org/x/sys/unix" "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) return unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
} }
// EnsureTempDirWithRootOwner создает каталог в /tmp/alr с правами для привилегированной группы // EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы
// Все каталоги в /tmp/alr принадлежат root:привилегированная_группа с правами 775 // Все каталоги в /tmp/alr и /var/cache/alr принадлежат root:привилегированная_группа с правами 2775
// Для других каталогов использует стандартные права // Для других каталогов использует стандартные права
func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error { func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error {
if strings.HasPrefix(path, "/tmp/alr") { needsElevation := strings.HasPrefix(path, "/tmp/alr") || strings.HasPrefix(path, "/var/cache/alr")
// Сначала создаем директорию обычным способом
err := os.MkdirAll(path, mode) if needsElevation {
if err != nil {
return err
}
// В CI или если мы уже root, не нужно использовать sudo // В CI или если мы уже root, не нужно использовать sudo
isRoot := os.Geteuid() == 0 isRoot := os.Geteuid() == 0
isCI := os.Getenv("CI") == "true" isCI := os.Getenv("CI") == "true"
// В CI создаем директории с обычными правами // В CI создаем директории с обычными правами
if isCI { if isCI {
err := os.MkdirAll(path, mode)
if err != nil {
return err
}
// В CI не используем группу wheel и не меняем права // В CI не используем группу wheel и не меняем права
// Устанавливаем базовые права 777 для временных каталогов // Устанавливаем базовые права 777 для временных каталогов
chmodCmd := exec.Command("chmod", "777", path) chmodCmd := exec.Command("chmod", "777", path)
chmodCmd.Run() // Игнорируем ошибки chmodCmd.Run() // Игнорируем ошибки
return nil return nil
} }
// Для обычной работы устанавливаем права и привилегированную группу // Для обычной работы устанавливаем права и привилегированную группу
permissions := "2775" permissions := "2775"
group := GetPrivilegedGroup() group := GetPrivilegedGroup()
var chmodCmd, chownCmd *exec.Cmd var mkdirCmd, chmodCmd, chownCmd *exec.Cmd
if isRoot { if isRoot {
// Выполняем команды напрямую без sudo // Выполняем команды напрямую без sudo
mkdirCmd = exec.Command("mkdir", "-p", path)
chmodCmd = exec.Command("chmod", permissions, path) chmodCmd = exec.Command("chmod", permissions, path)
chownCmd = exec.Command("chown", "root:"+group, path) chownCmd = exec.Command("chown", "root:"+group, path)
} else { } else {
// Используем sudo для обычных пользователей // Используем sudo для всех операций с привилегированными каталогами
mkdirCmd = exec.Command("sudo", "mkdir", "-p", path)
chmodCmd = exec.Command("sudo", "chmod", permissions, path) chmodCmd = exec.Command("sudo", "chmod", permissions, path)
chownCmd = exec.Command("sudo", "chown", "root:"+group, 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() err = chmodCmd.Run()
if err != nil { if err != nil {
// Для root игнорируем ошибки, если группа не существует
if !isRoot { if !isRoot {
return err return fmt.Errorf("не удалось установить права на %s: %w", path, err)
} }
} }
// Устанавливаем владельца root:wheel // Устанавливаем владельца root:группа
err = chownCmd.Run() err = chownCmd.Run()
if err != nil { if err != nil {
// Для root игнорируем ошибки, если группа не существует
if !isRoot { if !isRoot {
return err return fmt.Errorf("не удалось установить владельца на %s: %w", path, err)
} }
} }
return nil return nil
} }
// Для остальных каталогов обычное создание // Для остальных каталогов обычное создание
return os.MkdirAll(path, mode) 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
}