убрана лишняя зависимость bindfs и избыточное использование дополнительного пользователя alr

This commit is contained in:
2025-08-26 22:09:28 +03:00
parent ab41700004
commit 51fa7ca6fb
25 changed files with 274 additions and 405 deletions

View File

@@ -44,9 +44,9 @@ var HandshakeConfig = plugin.HandshakeConfig{
func setCommonCmdEnv(cmd *exec.Cmd) {
cmd.Env = []string{
"HOME=/var/cache/alr",
"LOGNAME=alr",
"USER=alr",
"HOME=" + os.Getenv("HOME"),
"LOGNAME=" + os.Getenv("USER"),
"USER=" + os.Getenv("USER"),
"PATH=/usr/bin:/bin:/usr/local/bin",
}
for _, env := range os.Environ() {
@@ -102,9 +102,7 @@ func getSafeExecutor[T any](subCommand, pluginName string) (T, func(), error) {
Cmd: cmd,
Logger: logger.GetHCLoggerAdapter(),
SkipHostEnv: true,
UnixSocketConfig: &plugin.UnixSocketConfig{
Group: "alr",
},
UnixSocketConfig: &plugin.UnixSocketConfig{},
SyncStderr: os.Stderr,
})
rpcClient, err := client.Client()

View File

@@ -23,6 +23,7 @@ import (
"os"
"strings"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/dl"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/dlcache"
)
@@ -74,7 +75,9 @@ func (s *SourceDownloader) DownloadSources(
}
}
opts.DlCache = dlcache.New(s.cfg.GetPaths().CacheDir)
// Используем временную директорию для загрузок
// dlcache.New добавит свой подкаталог "dl" внутри
opts.DlCache = dlcache.New(constants.TempDir)
err := dl.Download(ctx, opts)
if err != nil {

View File

@@ -40,6 +40,7 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cpu"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/manager"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/overrides"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/distro"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
@@ -47,15 +48,21 @@ import (
// Функция prepareDirs подготавливает директории для сборки.
func prepareDirs(dirs types.Directories) error {
err := os.RemoveAll(dirs.BaseDir) // Удаляем базовую директорию, если она существует
// Пробуем удалить базовую директорию, если она существует
err := os.RemoveAll(dirs.BaseDir)
if err != nil {
// Если не можем удалить (например, принадлежит root), игнорируем
// и попробуем создать новые директории
}
// Создаем директории с правильным владельцем для /tmp/alr с setgid битом
err = utils.EnsureTempDirWithRootOwner(dirs.SrcDir, 0o2775)
if err != nil {
return err
}
err = os.MkdirAll(dirs.SrcDir, 0o755) // Создаем директорию для источников
if err != nil {
return err
}
return os.MkdirAll(dirs.PkgDir, 0o755) // Создаем директорию для пакетов
// Создаем директорию для пакетов с setgid битом
return utils.EnsureTempDirWithRootOwner(dirs.PkgDir, 0o2775)
}
// Функция buildContents создает секцию содержимого пакета, которая содержит файлы,

View File

@@ -21,6 +21,7 @@ package config
import (
"fmt"
"os"
"path/filepath"
"github.com/goccy/go-yaml"
@@ -55,7 +56,12 @@ func defaultConfigKoanf() *koanf.Koanf {
"ignorePkgUpdates": []string{},
"logLevel": "info",
"autoPull": true,
"repos": []types.Repo{},
"repos": []types.Repo{
{
Name: "alr-default",
URL: "https://gitea.plemya-x.ru/Plemya-x/alr-default.git",
},
},
}
if err := k.Load(confmap.Provider(defaults, "."), nil); err != nil {
panic(k)
@@ -98,8 +104,15 @@ func (c *ALRConfig) Load() error {
c.paths.UserConfigPath = constants.SystemConfigPath
c.paths.CacheDir = constants.SystemCachePath
c.paths.RepoDir = filepath.Join(c.paths.CacheDir, "repo")
c.paths.PkgsDir = filepath.Join(c.paths.CacheDir, "pkgs")
c.paths.DBPath = filepath.Join(c.paths.CacheDir, "db")
c.paths.PkgsDir = filepath.Join(constants.TempDir, "pkgs") // Перемещаем в /tmp/alr/pkgs
c.paths.DBPath = filepath.Join(c.paths.CacheDir, "alr.db")
// Проверяем существование кэш-директории, но не пытаемся создать
if _, err := os.Stat(c.paths.CacheDir); err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("failed to check cache directory: %w", err)
}
}
return nil
}

View File

@@ -19,6 +19,6 @@ package constants
const (
SystemConfigPath = "/etc/alr/alr.toml"
SystemCachePath = "/var/cache/alr"
AlrRunDir = "/var/run/alr"
TempDir = "/tmp/alr"
PrivilegedGroup = "wheel"
)

View File

@@ -21,7 +21,10 @@ package db
import (
"context"
"fmt"
"log/slog"
"os"
"path/filepath"
"github.com/leonelquinteros/gotext"
_ "modernc.org/sqlite"
@@ -54,6 +57,21 @@ func New(config Config) *Database {
func (d *Database) Connect() error {
dsn := d.config.GetPaths().DBPath
// Проверяем директорию для БД
dbDir := filepath.Dir(dsn)
if _, err := os.Stat(dbDir); err != nil {
if os.IsNotExist(err) {
// Директория не существует - пытаемся создать
if mkErr := os.MkdirAll(dbDir, 0775); mkErr != nil {
// Не смогли создать - вернём ошибку, пользователь должен использовать alr fix
return fmt.Errorf("cache directory does not exist, please run 'alr fix' to create it: %w", mkErr)
}
} else {
return fmt.Errorf("failed to check database directory: %w", err)
}
}
engine, err := xorm.NewEngine("sqlite", dsn)
// engine.SetLogLevel(log.LOG_DEBUG)
// engine.ShowSQL(true)

View File

@@ -17,12 +17,9 @@
package utils
import (
"errors"
"os"
"os/exec"
"os/user"
"strconv"
"syscall"
"github.com/leonelquinteros/gotext"
"github.com/urfave/cli/v2"
@@ -32,114 +29,12 @@ import (
"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
)
func GetUidGidAlrUserString() (string, string, error) {
u, err := user.Lookup("alr")
if err != nil {
return "", "", err
}
return u.Uid, u.Gid, nil
}
func GetUidGidAlrUser() (int, int, error) {
strUid, strGid, err := GetUidGidAlrUserString()
if err != nil {
return 0, 0, err
}
uid, err := strconv.Atoi(strUid)
if err != nil {
return 0, 0, err
}
gid, err := strconv.Atoi(strGid)
if err != nil {
return 0, 0, err
}
return uid, gid, nil
}
func DropCapsToAlrUser() error {
uid, gid, err := GetUidGidAlrUser()
if err != nil {
return err
}
err = syscall.Setgid(gid)
if err != nil {
return err
}
err = syscall.Setuid(uid)
if err != nil {
return err
}
return EnsureIsAlrUser()
}
func ExitIfCantDropGidToAlr() cli.ExitCoder {
_, gid, err := GetUidGidAlrUser()
if err != nil {
return cliutils.FormatCliExit("cannot get gid alr", err)
}
err = syscall.Setgid(gid)
if err != nil {
return cliutils.FormatCliExit("cannot get setgid alr", err)
}
return nil
}
// ExitIfCantDropCapsToAlrUser attempts to drop capabilities to the already
// running user. Returns a cli.ExitCoder with an error if the operation fails.
// See also [ExitIfCantDropCapsToAlrUserNoPrivs] for a version that also applies
// no-new-privs.
func ExitIfCantDropCapsToAlrUser() cli.ExitCoder {
err := DropCapsToAlrUser()
if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error on dropping capabilities"), err)
}
return nil
}
func ExitIfCantSetNoNewPrivs() cli.ExitCoder {
if err := NoNewPrivs(); err != nil {
return cliutils.FormatCliExit("error on NoNewPrivs", err)
}
return nil
}
// ExitIfCantDropCapsToAlrUserNoPrivs combines [ExitIfCantDropCapsToAlrUser] with [ExitIfCantSetNoNewPrivs]
func ExitIfCantDropCapsToAlrUserNoPrivs() cli.ExitCoder {
if err := ExitIfCantDropCapsToAlrUser(); err != nil {
return err
}
if err := ExitIfCantSetNoNewPrivs(); err != nil {
return err
}
return nil
}
// IsNotRoot проверяет, что текущий пользователь не является root
func IsNotRoot() bool {
return os.Getuid() != 0
}
func EnsureIsAlrUser() error {
uid, gid, err := GetUidGidAlrUser()
if err != nil {
return err
}
newUid := syscall.Getuid()
if newUid != uid {
return errors.New("uid don't matches requested")
}
newGid := syscall.Getgid()
if newGid != gid {
return errors.New("gid don't matches requested")
}
return nil
}
// EnuseIsPrivilegedGroupMember проверяет, что пользователь является членом привилегированной группы (wheel)
func EnuseIsPrivilegedGroupMember() error {
currentUser, err := user.Current()
if err != nil {
@@ -164,26 +59,6 @@ func EnuseIsPrivilegedGroupMember() error {
return cliutils.FormatCliExit(gotext.Get("You need to be a %s member to perform this action", constants.PrivilegedGroup), nil)
}
func EscalateToRootGid() error {
return syscall.Setgid(0)
}
func EscalateToRootUid() error {
return syscall.Setuid(0)
}
func EscalateToRoot() error {
err := EscalateToRootUid()
if err != nil {
return err
}
err = EscalateToRootGid()
if err != nil {
return err
}
return nil
}
func RootNeededAction(f cli.ActionFunc) cli.ActionFunc {
return func(ctx *cli.Context) error {
deps, err := appbuilder.

View File

@@ -16,8 +16,44 @@
package utils
import "golang.org/x/sys/unix"
import (
"os"
"os/exec"
"strings"
"golang.org/x/sys/unix"
)
func NoNewPrivs() error {
return unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
}
// EnsureTempDirWithRootOwner создает каталог в /tmp/alr с правами для группы wheel
// Все каталоги в /tmp/alr принадлежат root:wheel с правами 775
// Для других каталогов использует стандартные права
func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error {
if strings.HasPrefix(path, "/tmp/alr") {
// Сначала создаем директорию обычным способом
err := os.MkdirAll(path, mode)
if err != nil {
return err
}
// Все каталоги в /tmp/alr доступны для группы wheel
// Устанавливаем setgid бит (2775), чтобы новые файлы наследовали группу
permissions := "2775"
group := "wheel"
// Устанавливаем права с setgid битом
err = exec.Command("sudo", "chmod", permissions, path).Run()
if err != nil {
return err
}
// Устанавливаем владельца root:wheel
return exec.Command("sudo", "chown", "root:"+group, path).Run()
}
// Для остальных каталогов обычное создание
return os.MkdirAll(path, mode)
}