wip
This commit is contained in:
218
internal.go
218
internal.go
@@ -17,13 +17,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
@@ -32,13 +33,13 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils"
|
||||
appbuilder "gitea.plemya-x.ru/Plemya-x/ALR/internal/cliutils/app_builder"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
|
||||
database "gitea.plemya-x.ru/Plemya-x/ALR/internal/db"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/constants"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/logger"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/internal/utils"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/build"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/manager"
|
||||
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/repos"
|
||||
)
|
||||
|
||||
func InternalBuildCmd() *cli.Command {
|
||||
@@ -48,13 +49,15 @@ func InternalBuildCmd() *cli.Command {
|
||||
Hidden: true,
|
||||
Action: func(c *cli.Context) error {
|
||||
logger.SetupForGoPlugin()
|
||||
err := utils.DropCapsToAlrUser()
|
||||
if err != nil {
|
||||
slog.Error("aa", "err", err)
|
||||
os.Exit(1)
|
||||
|
||||
slog.Debug("start _internal-safe-script-executor", "uid", syscall.Getuid(), "gid", syscall.Getgid())
|
||||
|
||||
if err := utils.ExitIfCantDropCapsToAlrUser(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := config.New()
|
||||
err = cfg.Load()
|
||||
err := cfg.Load()
|
||||
if err != nil {
|
||||
return cliutils.FormatCliExit(gotext.Get("Error loading config"), err)
|
||||
}
|
||||
@@ -66,6 +69,7 @@ func InternalBuildCmd() *cli.Command {
|
||||
JSONFormat: false,
|
||||
DisableTime: true,
|
||||
})
|
||||
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: build.HandshakeConfig,
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
@@ -87,24 +91,28 @@ func InternalInstallCmd() *cli.Command {
|
||||
Hidden: true,
|
||||
Action: func(c *cli.Context) error {
|
||||
logger.SetupForGoPlugin()
|
||||
err := syscall.Setuid(0)
|
||||
if err != nil {
|
||||
slog.Error("err")
|
||||
os.Exit(1)
|
||||
|
||||
if err := utils.EnuseIsAlrUser(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := config.New()
|
||||
err = cfg.Load()
|
||||
// Before escalating the rights, we made sure that
|
||||
// this is an ALR user, so it looks safe.
|
||||
err := utils.EscalateToRootUid()
|
||||
if err != nil {
|
||||
return cliutils.FormatCliExit(gotext.Get("Error loading config"), err)
|
||||
return cliutils.FormatCliExit("cannot escalate to root", err)
|
||||
}
|
||||
|
||||
db := database.New(cfg)
|
||||
rs := repos.New(cfg, db)
|
||||
err = db.Init(c.Context)
|
||||
deps, err := appbuilder.
|
||||
New(c.Context).
|
||||
WithConfig().
|
||||
WithDB().
|
||||
WithReposNoPull().
|
||||
Build()
|
||||
if err != nil {
|
||||
return cliutils.FormatCliExit(gotext.Get("Error initialization database"), err)
|
||||
return err
|
||||
}
|
||||
defer deps.Defer()
|
||||
|
||||
logger := hclog.New(&hclog.LoggerOptions{
|
||||
Name: "plugin",
|
||||
@@ -119,7 +127,7 @@ func InternalInstallCmd() *cli.Command {
|
||||
Plugins: map[string]plugin.Plugin{
|
||||
"installer": &build.InstallerPlugin{
|
||||
Impl: build.NewInstaller(
|
||||
rs,
|
||||
deps.Repos,
|
||||
manager.Detect(),
|
||||
),
|
||||
},
|
||||
@@ -131,52 +139,100 @@ func InternalInstallCmd() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func Mount(target string) (string, func(), error) {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to get executable path: %w", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(exe, "_internal-temporary-mount", target)
|
||||
|
||||
stdoutPipe, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to get stdout pipe: %w", err)
|
||||
}
|
||||
|
||||
stdinPipe, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to get stdin pipe: %w", err)
|
||||
}
|
||||
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return "", nil, fmt.Errorf("failed to start mount: %w", err)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(stdoutPipe)
|
||||
var mountPath string
|
||||
if scanner.Scan() {
|
||||
mountPath = scanner.Text()
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
_ = cmd.Process.Kill()
|
||||
return "", nil, fmt.Errorf("failed to read mount output: %w", err)
|
||||
}
|
||||
|
||||
if mountPath == "" {
|
||||
_ = cmd.Process.Kill()
|
||||
return "", nil, errors.New("mount failed: no target path returned")
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
slog.Debug("cleanup triggered")
|
||||
_, _ = fmt.Fprintln(stdinPipe, "")
|
||||
_ = cmd.Wait()
|
||||
}
|
||||
|
||||
return mountPath, cleanup, nil
|
||||
}
|
||||
|
||||
func InternalMountCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "_internal-mount",
|
||||
Name: "_internal-temporary-mount",
|
||||
HideHelp: true,
|
||||
Hidden: true,
|
||||
Action: func(c *cli.Context) error {
|
||||
logger.SetupForGoPlugin()
|
||||
|
||||
sourceDir := c.Args().First()
|
||||
|
||||
u, _ := user.Current()
|
||||
_, alrGid, _ := utils.GetUidGidAlrUser()
|
||||
|
||||
logger.SetupForGoPlugin()
|
||||
err := syscall.Setuid(0)
|
||||
if err != nil {
|
||||
slog.Error("Failed to setuid(0)", "err", err)
|
||||
os.Exit(1)
|
||||
if err := utils.EnuseIsWheelMember(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
alrRunDir := "/var/run/alr"
|
||||
err = os.MkdirAll(alrRunDir, 0o750)
|
||||
if err != nil {
|
||||
slog.Error("Error creating /var/run/alr directory", "err", err)
|
||||
os.Exit(1)
|
||||
// Before escalating the rights, we made sure that
|
||||
// 1. user in wheel group
|
||||
// 2. user can access sourceDir
|
||||
if err := utils.EscalateToRootUid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := syscall.Setgid(alrGid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, gid, _ := utils.GetUidGidAlrUser()
|
||||
|
||||
// Меняем группу на alr и права
|
||||
err = os.Chown(alrRunDir, 0, gid) // root:alr
|
||||
if err != nil {
|
||||
slog.Error("Failed to chown /var/run/alr", "err", err)
|
||||
os.Exit(1)
|
||||
if err := os.MkdirAll(constants.AlrRunDir, 0o770); err != nil {
|
||||
return cliutils.FormatCliExit(fmt.Sprintf("failed to create %s", constants.AlrRunDir), err)
|
||||
}
|
||||
|
||||
// Создаем поддиректорию для bindfs
|
||||
targetDir := filepath.Join(alrRunDir, fmt.Sprintf("bindfs-%d", os.Getpid()))
|
||||
err = os.MkdirAll(targetDir, 0o750) // 0750: владелец (root) и группа (alr) имеют доступ
|
||||
if err != nil {
|
||||
slog.Error("Error creating bindfs target directory", "err", err)
|
||||
os.Exit(1)
|
||||
if err := os.Chown(constants.AlrRunDir, 0, alrGid); err != nil {
|
||||
return cliutils.FormatCliExit(fmt.Sprintf("failed to chown %s", constants.AlrRunDir), err)
|
||||
}
|
||||
|
||||
// Устанавливаем владельца и группу (root:alr)
|
||||
err = os.Chown(targetDir, 0, gid)
|
||||
if err != nil {
|
||||
slog.Error("Failed to chown bindfs directory", "err", err)
|
||||
os.Exit(1)
|
||||
targetDir := filepath.Join(constants.AlrRunDir, fmt.Sprintf("bindfs-%d", os.Getpid()))
|
||||
// 0750: owner (root) and group (alr)
|
||||
if err := os.MkdirAll(targetDir, 0o750); err != nil {
|
||||
return cliutils.FormatCliExit("error creating bindfs target directory", err)
|
||||
}
|
||||
|
||||
// chown AlrRunDir/mounts/bindfs-* to (root:alr),
|
||||
// so alr user can access dir
|
||||
if err := os.Chown(targetDir, 0, alrGid); err != nil {
|
||||
return cliutils.FormatCliExit("failed to chown bindfs directory", err)
|
||||
}
|
||||
|
||||
bindfsCmd := exec.Command(
|
||||
@@ -188,74 +244,24 @@ func InternalMountCmd() *cli.Command {
|
||||
|
||||
bindfsCmd.Stderr = os.Stderr
|
||||
|
||||
if err := bindfsCmd.Start(); err != nil {
|
||||
slog.Error("Error starting bindfs", "err", err)
|
||||
os.Exit(1)
|
||||
if err := bindfsCmd.Run(); err != nil {
|
||||
return cliutils.FormatCliExit("failed to strart bindfs", err)
|
||||
}
|
||||
|
||||
fmt.Print(targetDir)
|
||||
fmt.Println(targetDir)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
_, _ = bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
|
||||
func InternalUnmountCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "_internal-umount",
|
||||
HideHelp: true,
|
||||
Hidden: true,
|
||||
Action: func(c *cli.Context) error {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
slog.Error("Failed to get current user", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
uid, gid, err := utils.GetUidGidAlrUserString()
|
||||
if err != nil {
|
||||
slog.Error("Failed to get alr user info", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if currentUser.Uid != uid && currentUser.Gid != gid {
|
||||
slog.Error("Only alr user can unmount these directories")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
targetDir := c.Args().First()
|
||||
if targetDir == "" {
|
||||
slog.Error("No target directory specified")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(targetDir, "/var/run/alr/") {
|
||||
slog.Error("Can only unmount directories under /var/run/alr")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(targetDir); os.IsNotExist(err) {
|
||||
slog.Error("Target directory does not exist", "dir", targetDir)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = syscall.Setuid(0)
|
||||
if err != nil {
|
||||
slog.Error("Failed to setuid(0)", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
slog.Debug("start unmount", "dir", targetDir)
|
||||
|
||||
umountCmd := exec.Command("umount", targetDir)
|
||||
umountCmd.Stderr = os.Stderr
|
||||
|
||||
if err := umountCmd.Run(); err != nil {
|
||||
slog.Error("Error unmounting directory", "dir", targetDir, "err", err)
|
||||
os.Exit(1)
|
||||
return cliutils.FormatCliExit(fmt.Sprintf("failed to unmount %s", targetDir), err)
|
||||
}
|
||||
|
||||
if err := os.Remove(targetDir); err != nil {
|
||||
slog.Error("Error removing directory", "dir", targetDir, "err", err)
|
||||
os.Exit(1)
|
||||
return cliutils.FormatCliExit(fmt.Sprintf("error removing directory %s", targetDir), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user