Skip to content

Commit

Permalink
feat: selective environment variable pass and working directory set
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmetozer committed Jun 29, 2024
1 parent f1a0ddc commit 434dfb0
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 24 deletions.
85 changes: 62 additions & 23 deletions pkg/cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cmd
import (
"flag"
"fmt"
"log"
"log/slog"
"os"
"os/exec"

Expand All @@ -22,10 +22,16 @@ func execOnContainer(args []string) error {
f := flag.NewFlagSet("exec", flag.ExitOnError)

var (
help bool
help bool
EnvAll bool
PassEnv config.StringFlags
Dir string
)

f.BoolVar(&help, "help", false, "show this help message")
f.BoolVar(&EnvAll, "env-all", false, "send all enviroment variables to container")
f.StringVar(&Dir, "dir", "", "working directory")
f.Var(&PassEnv, "env-pass", "pass only requested enviroment variables to container")

if err := f.Parse(thisFlags); err != nil {
return fmt.Errorf("error parsing flags: %v", err)
Expand All @@ -37,49 +43,58 @@ func execOnContainer(args []string) error {

conts, err := config.AllContainers()
if err != nil {
log.Fatalf("Failed to get containers: %v", err)
return fmt.Errorf("failed to get containers: %v", err)
}
c, err := config.GetByName(&conts, args[0])
if err != nil {
log.Fatalf("Failed to get container %s: %v", args[0], err)
return fmt.Errorf("failed to get container %s: %v", args[0], err)
}

var nsFuncs []func()
type nsConf struct {
nsname string
CloneFlag int
}

var nsFuncs []nsConf

Cloneflags := unix.CLONE_NEWIPC | unix.CLONE_NEWNS | unix.CLONE_NEWCGROUP

if c.NS["pid"].Value != "host" {
Cloneflags |= unix.CLONE_NEWPID
nsFuncs = append(nsFuncs, func() { setNs("pid", c.ContPid, unix.CLONE_NEWPID) })
nsFuncs = append(nsFuncs, nsConf{"pid", unix.CLONE_NEWPID})
}
if c.NS["net"].Value != "host" {
Cloneflags |= unix.CLONE_NEWNET
nsFuncs = append(nsFuncs, func() { setNs("net", c.ContPid, unix.CLONE_NEWNET) })
nsFuncs = append(nsFuncs, nsConf{"net", unix.CLONE_NEWNET})
}
if c.NS["user"].Value != "host" {
Cloneflags |= unix.CLONE_NEWUSER
nsFuncs = append(nsFuncs, func() { setNs("user", c.ContPid, unix.CLONE_NEWUSER) })
nsFuncs = append(nsFuncs, nsConf{"user", unix.CLONE_NEWUSER})
}
if c.NS["uts"].Value != "host" {
Cloneflags |= unix.CLONE_NEWUTS
nsFuncs = append(nsFuncs, func() { setNs("uts", c.ContPid, unix.CLONE_NEWUTS) })
nsFuncs = append(nsFuncs, nsConf{"uts", unix.CLONE_NEWUTS})
}

nsFuncs = append(nsFuncs, nsConf{"pid", unix.CLONE_NEWPID})
nsFuncs = append(nsFuncs, nsConf{"cgroup", unix.CLONE_NEWCGROUP})
nsFuncs = append(nsFuncs, nsConf{"mnt", unix.CLONE_NEWNS})

// Unshare the namespaces
if err := unix.Unshare(Cloneflags); err != nil {
log.Fatalf("Failed to unshare namespaces: %v", err)
return fmt.Errorf("unshare namespaces: %v", err)
}
for _, f := range nsFuncs {
f()
// Set the namespaces
for _, nsConf := range nsFuncs {
if err := setNs(nsConf.nsname, c.ContPid, nsConf.CloneFlag); err != nil {
return err
}
}

setNs("pid", c.ContPid, unix.CLONE_NEWPID)
setNs("cgroup", c.ContPid, unix.CLONE_NEWCGROUP)
setNs("mnt", c.ContPid, unix.CLONE_NEWNS)

// Set the hostname
if err := unix.Sethostname([]byte(c.Name)); err != nil {
log.Fatalf("Failed to set hostname %s: %v", c.Name, err)
return fmt.Errorf("set hostname %s: %v", c.Name, err)
}

var cmd *exec.Cmd
switch len(childArgs) {
case 0:
Expand All @@ -93,7 +108,31 @@ func execOnContainer(args []string) error {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
cmd.Dir = c.Dir
if Dir != "" {
cmd.Dir = Dir
}

cmd.Env = []string{}
if EnvAll {
cmd.Env = os.Environ()
} else {
pathIsSet := false
for _, env := range PassEnv {
if env == "PATH" {
pathIsSet = true
}
variable := os.Getenv(env)
if variable == "" {
slog.Info("enviroment variable not found", "variable", env)
} else {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", env, variable))
}
}
if !pathIsSet {
cmd.Env = append(cmd.Env, fmt.Sprintf("PATH=%s", os.Getenv("PATH")))
}
}

err = cmd.Run()
if err != nil {
Expand All @@ -103,16 +142,16 @@ func execOnContainer(args []string) error {
return nil
}

func setNs(nsname string, pid, nstype int) {
func setNs(nsname string, pid, nstype int) error {
path := fmt.Sprintf("/proc/%d/ns/%s", pid, nsname)
file, err := os.Open(path)
if err != nil {
log.Fatalf("Failed to open namespace file %s: %v", path, err)
return fmt.Errorf("failed to open namespace file %s: %v", path, err)
}
// defer file.Close()

// Set the namespace
if err := unix.Setns(int(file.Fd()), nstype); err != nil {
log.Fatalf("Failed to set namespace %s: %v", nsname, err)
return fmt.Errorf("failed to set namespace %s: %v", nsname, err)
}
return nil
}
2 changes: 2 additions & 0 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func run(args []string) error {
f.BoolVar(&c.Startup, "startup", false, "run container at startup by sandal daemon")

f.BoolVar(&c.EnvAll, "env-all", false, "send all enviroment variables to container")
f.Var(&c.PassEnv, "env-pass", "pass only requested enviroment variables to container")
f.StringVar(&c.Dir, "dir", "", "working directory")

f.UintVar(&c.TmpSize, "tmp", 0, "allocate changes at memory instead of disk. unit is in MB, disk is used used by default")

Expand Down
2 changes: 2 additions & 0 deletions pkg/config/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,14 @@ type Config struct {
Resolv string
Hosts string
Status string
Dir string
Volumes StringFlags
HostArgs []string
PodArgs []string
LowerDirs StringFlags
RunPreExec StringFlags
RunPrePivot StringFlags
PassEnv StringFlags

Ifaces []NetIface
}
Expand Down
20 changes: 19 additions & 1 deletion pkg/container/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package container
import (
"encoding/binary"
"fmt"
"log/slog"
"os"
"os/exec"
"os/signal"
Expand Down Expand Up @@ -133,7 +134,24 @@ func childEnv(c *config.Config) []string {
if c.EnvAll {
return append(os.Environ(), CHILD_CONFIG_ENV_NAME+"="+c.ConfigFileLoc())
}
return []string{CHILD_CONFIG_ENV_NAME + "=" + c.ConfigFileLoc()}
envVars := []string{}
pathIsSet := false
for _, env := range c.PassEnv {
if env == "PATH" {
pathIsSet = true
}
variable := os.Getenv(env)
if variable == "" {
slog.Info("enviroment variable not found", "variable", env)
} else {
envVars = append(envVars, fmt.Sprintf("%s=%s", env, variable))
}
}
if !pathIsSet {
envVars = append(envVars, fmt.Sprintf("PATH=%s", os.Getenv("PATH")))
}

return append(envVars, CHILD_CONFIG_ENV_NAME+"="+c.ConfigFileLoc())
}

func childArgs(args []string) (string, []string) {
Expand Down

0 comments on commit 434dfb0

Please sign in to comment.