3 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
42f0d5e575 Исправление дублирования "alr" в названии пакета
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m6s
Create Release / changelog (push) Successful in 3m7s
2025-09-21 13:43:36 +03:00
7b9404a058 Исправление обработки зависимостей на debian-based
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m28s
Create Release / changelog (push) Successful in 3m6s
2025-09-21 12:36:48 +03:00
5 changed files with 111 additions and 63 deletions

View File

@@ -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
}

View File

@@ -176,8 +176,13 @@ func getBasePkgInfo(vars *alrsh.Package, input interface {
OsInfoProvider
},
) *nfpm.Info {
repo := input.Repository()
// Избегаем дублирования "alr-" префикса
if strings.HasPrefix(repo, "alr-") {
repo = repo[4:] // убираем "alr-" префикс
}
return &nfpm.Info{
Name: fmt.Sprintf("%s+alr-%s", vars.Name, input.Repository()),
Name: fmt.Sprintf("%s+alr-%s", vars.Name, repo),
Arch: cpu.Arch(),
Version: vars.Version,
Release: overrides.ReleasePlatformSpecific(vars.Release, input.OSRelease()),

View File

@@ -140,16 +140,19 @@ func (a *APT) ListInstalled(opts *Opts) (map[string]string, error) {
}
func (a *APT) IsInstalled(pkg string) (bool, error) {
cmd := exec.Command("dpkg-query", "-l", pkg)
cmd := exec.Command("dpkg-query", "-f", "${Status}", "-W", pkg)
output, err := cmd.CombinedOutput()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
// Exit code 1 means the package is not installed
// Код выхода 1 означает что пакет не найден
if exitErr.ExitCode() == 1 {
return false, nil
}
}
return false, fmt.Errorf("apt: isinstalled: %w, output: %s", err, output)
}
return true, nil
status := strings.TrimSpace(string(output))
// Проверяем что пакет действительно установлен (статус должен содержать "install ok installed")
return strings.Contains(status, "install ok installed"), nil
}

View File

@@ -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 {

View File

@@ -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
}