From 79975293950af6915ffa8b419c48d882b551eb33 Mon Sep 17 00:00:00 2001 From: axtloss Date: Sat, 10 Aug 2024 02:41:12 +0200 Subject: [PATCH] no longer constantly run as root no more possible security vulnerabilities :pensive: --- cmd/compile.go | 2 +- cmd/root.go | 33 +++++++++++++++++++++++++++++++++ core/compile.go | 21 +++++++++++++++------ core/plugins.in | 13 +++++++++++-- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/cmd/compile.go b/cmd/compile.go index 20df79d..c550f5d 100644 --- a/cmd/compile.go +++ b/cmd/compile.go @@ -60,7 +60,7 @@ func compileCommand(cmd *cobra.Command, args []string) error { runtime = detectedRuntime } - err := core.CompileRecipe(recipePath, runtime) + err := core.CompileRecipe(recipePath, runtime, IsRoot, OrigGID, OrigUID) if err != nil { return err } diff --git a/cmd/root.go b/cmd/root.go index aa697b0..be13b65 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,10 +1,20 @@ package cmd import ( + "fmt" + "os" + "path/filepath" + "strconv" + "syscall" + "github.com/spf13/cobra" ) var Version = "0.7.4" +var IsRoot = false +var OrigUID = 1000 +var OrigGID = 1000 +var OrigUser = "user" var rootCmd = &cobra.Command{ Use: "vib", @@ -20,5 +30,28 @@ func init() { } func Execute() error { + if os.Getuid() == 0 { + IsRoot = true + gid, err := strconv.Atoi(os.Getenv("SUDO_GID")) + if err != nil { + return fmt.Errorf("failed to get user uid through SUDO_UID: %s", err.Error()) + } + OrigGID = gid // go moment?? + uid, err := strconv.Atoi(os.Getenv("SUDO_UID")) + if err != nil { + return fmt.Errorf("failed to get user uid through SUDO_GID: %s", err.Error()) + } + OrigUID = uid + user := os.Getenv("SUDO_USER") + os.Setenv("HOME", filepath.Join("/home", user)) + err = syscall.Seteuid(OrigUID) + if err != nil { + fmt.Println("WARN: Failed to drop root privileges") + } + err = syscall.Setgid(OrigGID) + if err != nil { + fmt.Println("WARN: Failed to drop root privileges") + } + } return rootCmd.Execute() } diff --git a/core/compile.go b/core/compile.go index 2050e8d..3a5784a 100644 --- a/core/compile.go +++ b/core/compile.go @@ -4,26 +4,29 @@ import ( "fmt" "os" "os/exec" + "syscall" "github.com/mitchellh/mapstructure" "github.com/vanilla-os/vib/api" ) // CompileRecipe compiles a recipe into a runnable image. -func CompileRecipe(recipePath string, runtime string) error { +func CompileRecipe(recipePath string, runtime string, isRoot bool, origGid int, origUid int) error { recipe, err := BuildRecipe(recipePath) if err != nil { return err } + syscall.Seteuid(0) + syscall.Setegid(0) switch runtime { case "docker": - err = compileDocker(recipe) + err = compileDocker(recipe, origGid, origUid) if err != nil { return err } case "podman": - err = compilePodman(recipe) + err = compilePodman(recipe, origGid, origUid) if err != nil { return err } @@ -32,6 +35,8 @@ func CompileRecipe(recipePath string, runtime string) error { default: return fmt.Errorf("no runtime specified and the prometheus library is not implemented yet") } + syscall.Seteuid(origUid) + syscall.Setegid(origGid) for _, finalizeInterface := range recipe.Finalize { var module Finalize @@ -40,7 +45,7 @@ func CompileRecipe(recipePath string, runtime string) error { if err != nil { return err } - err = LoadFinalizePlugin(module.Type, finalizeInterface, &recipe, runtime) + err = LoadFinalizePlugin(module.Type, finalizeInterface, &recipe, runtime, isRoot, origGid, origUid) if err != nil { return err } @@ -51,7 +56,7 @@ func CompileRecipe(recipePath string, runtime string) error { return nil } -func compileDocker(recipe api.Recipe) error { +func compileDocker(recipe api.Recipe, gid int, uid int) error { docker, err := exec.LookPath("docker") if err != nil { return err @@ -66,11 +71,13 @@ func compileDocker(recipe api.Recipe) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Dir = recipe.ParentPath + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} return cmd.Run() } -func compilePodman(recipe api.Recipe) error { +func compilePodman(recipe api.Recipe, gid int, uid int) error { podman, err := exec.LookPath("podman") if err != nil { return err @@ -85,6 +92,8 @@ func compilePodman(recipe api.Recipe) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Dir = recipe.ParentPath + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} return cmd.Run() } diff --git a/core/plugins.in b/core/plugins.in index b4c2fd7..83a871a 100644 --- a/core/plugins.in +++ b/core/plugins.in @@ -11,6 +11,7 @@ import ( ) import ( "os" + "syscall" ) var openedBuildPlugins map[string]Plugin @@ -21,7 +22,7 @@ func LoadPlugin(name string, plugintype api.PluginType, recipe *api.Recipe) (uin localPluginPath := fmt.Sprintf("%s/%s.so", recipe.PluginPath, name) - globalPluginPath := fmt.Sprintf("%INSTALLPREFIX%/share/vib/plugins/%s.so", name) + globalPluginPath := fmt.Sprintf("%INSTALLPREFIX%/share/vib/plugins/%s.so", name) // Prefer local plugins before global ones var loadedPlugin uintptr @@ -106,7 +107,7 @@ func LoadBuildPlugin(name string, module interface{}, recipe *api.Recipe) (strin } } -func LoadFinalizePlugin(name string, module interface{}, recipe *api.Recipe, runtime string) error { +func LoadFinalizePlugin(name string, module interface{}, recipe *api.Recipe, runtime string, isRoot bool, origGid int, origUid int) error { if openedFinalizePlugins == nil { openedFinalizePlugins = make(map[string]Plugin) } @@ -127,6 +128,9 @@ func LoadFinalizePlugin(name string, module interface{}, recipe *api.Recipe, run } fmt.Printf("Using Finalize plugin: %s\n", finalizeModule.Name) + syscall.Seteuid(0) + syscall.Setegid(0) + var getPluginScope func() int32 purego.RegisterLibFunc(&getPluginScope, finalizeModule.LoadedPlugin, "PluginScope") scope := getPluginScope() @@ -153,6 +157,9 @@ func LoadFinalizePlugin(name string, module interface{}, recipe *api.Recipe, run scopedata.Runtime = runtime } if scope&api.FS == api.FS { + if !isRoot { + return fmt.Errorf("Plugin %s requires scope api.FS, which requires vib to run as root", finalizeModule.Name) + } imageID, err := GetImageID(imageName, containerStorage) if err != nil { return err @@ -172,6 +179,8 @@ func LoadFinalizePlugin(name string, module interface{}, recipe *api.Recipe, run return err } res := finalizeModule.BuildFunc(C.CString(string(moduleJson)), C.CString(string(scopeJson))) + syscall.Seteuid(origGid) + syscall.Setegid(origUid) if strings.HasPrefix(res, "ERROR:") { return fmt.Errorf("%s", strings.Replace(res, "ERROR: ", "", 1)) } else {