From 4f923f14b9e554dbc79ba964adba5dfb23802b5b Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Wed, 23 Mar 2016 11:45:03 -0700 Subject: [PATCH 1/2] Pass environment variables from host to exec based tasks --- client/config/config.go | 12 ++++++++++++ client/driver/env/env.go | 24 ++++++++++++++++++++++++ client/driver/env/env_test.go | 21 +++++++++++++++++++++ client/driver/exec.go | 8 +++++++- client/driver/java.go | 5 +++++ client/driver/raw_exec.go | 5 +++++ website/source/docs/agent/config.html.md | 11 +++++++++++ 7 files changed, 85 insertions(+), 1 deletion(-) diff --git a/client/config/config.go b/client/config/config.go index ac0968eff1ff..702d9a5e28da 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -10,6 +10,18 @@ import ( "github.com/hashicorp/nomad/nomad/structs" ) +var ( + // DefaultEnvBlacklist is the default set of environment variables that are + // filtered when passing the environment variables of the host to a task. + DefaultEnvBlacklist = strings.Join([]string{ + "CONSUL_TOKEN", + "VAULT_TOKEN", + "ATLAS_TOKEN", + "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", + "GOOGLE_APPLICATION_CREDENTIALS", + }, ",") +) + // RPCHandler can be provided to the Client if there is a local server // to avoid going over the network. If not provided, the Client will // maintain a connection pool to the servers diff --git a/client/driver/env/env.go b/client/driver/env/env.go index 447836ec19cb..ece460043ee6 100644 --- a/client/driver/env/env.go +++ b/client/driver/env/env.go @@ -2,6 +2,7 @@ package env import ( "fmt" + "os" "strconv" "strings" @@ -305,6 +306,29 @@ func (t *TaskEnvironment) AppendEnvvars(m map[string]string) *TaskEnvironment { return t } +func (t *TaskEnvironment) AppendHostEnvvars(filter []string) *TaskEnvironment { + hostEnv := os.Environ() + if t.Env == nil { + t.Env = make(map[string]string, len(hostEnv)) + } + + // Index the filtered environment variables. + index := make(map[string]struct{}, len(filter)) + for _, f := range filter { + index[f] = struct{}{} + } + + for _, e := range hostEnv { + parts := strings.Split(e, "=") + key, value := parts[0], parts[1] + if _, filtered := index[key]; !filtered { + t.Env[key] = value + } + } + + return t +} + func (t *TaskEnvironment) ClearEnvvars() *TaskEnvironment { t.Env = nil return t diff --git a/client/driver/env/env_test.go b/client/driver/env/env_test.go index de2d6805d899..f2dcbccfa546 100644 --- a/client/driver/env/env_test.go +++ b/client/driver/env/env_test.go @@ -2,8 +2,10 @@ package env import ( "fmt" + "os" "reflect" "sort" + "strings" "testing" "github.com/hashicorp/nomad/nomad/mock" @@ -204,3 +206,22 @@ func TestEnvironment_Interprolate(t *testing.T) { t.Fatalf("env.List() returned %v; want %v", act, exp) } } + +func TestEnvironment_AppendHostEnvVars(t *testing.T) { + host := os.Environ() + if len(host) < 2 { + t.Skip("No host environment variables. Can't test") + } + skip := strings.Split(host[0], "=")[0] + env := testTaskEnvironment(). + AppendHostEnvvars([]string{skip}). + Build() + + act := env.EnvMap() + if len(act) < 1 { + t.Fatalf("Host environment variables not properly set") + } + if _, ok := act[skip]; ok { + t.Fatalf("Didn't filter environment variable %q", skip) + } +} diff --git a/client/driver/exec.go b/client/driver/exec.go index 672f0e96cb01..2e898b8f195e 100644 --- a/client/driver/exec.go +++ b/client/driver/exec.go @@ -6,6 +6,7 @@ import ( "log" "os/exec" "path/filepath" + "strings" "syscall" "time" @@ -74,13 +75,18 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } + // Get the command to be ran command := driverConfig.Command if err := validateCommand(command, "args"); err != nil { return nil, err } - // Create a location to download the artifact. + // Set the host environment variables. + filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") + d.taskEnv.AppendHostEnvvars(filter) + + // Get the task directory for storing the executor logs. taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) diff --git a/client/driver/java.go b/client/driver/java.go index d4863df57c9c..eaa2a01d5bdc 100644 --- a/client/driver/java.go +++ b/client/driver/java.go @@ -117,6 +117,11 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { return nil, err } + + // Set the host environment variables. + filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") + d.taskEnv.AppendHostEnvvars(filter) + taskDir, ok := ctx.AllocDir.TaskDirs[d.DriverContext.taskName] if !ok { return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) diff --git a/client/driver/raw_exec.go b/client/driver/raw_exec.go index 2dc0aceebbd0..4143cb4dfc7f 100644 --- a/client/driver/raw_exec.go +++ b/client/driver/raw_exec.go @@ -6,6 +6,7 @@ import ( "log" "os/exec" "path/filepath" + "strings" "time" "github.com/hashicorp/go-plugin" @@ -82,6 +83,10 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl return nil, err } + // Set the host environment variables. + filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") + d.taskEnv.AppendHostEnvvars(filter) + bin, err := discover.NomadExecutable() if err != nil { return nil, fmt.Errorf("unable to find the nomad binary: %v", err) diff --git a/website/source/docs/agent/config.html.md b/website/source/docs/agent/config.html.md index 2590defe0fe0..9ceed03fe95f 100644 --- a/website/source/docs/agent/config.html.md +++ b/website/source/docs/agent/config.html.md @@ -370,6 +370,17 @@ documentation [here](/docs/drivers/index.html) If the whitelist is empty, all drivers are fingerprinted and enabled where applicable. +* `env.blacklist`: Nomad passes the host environment variables to `exec`, + `raw_exec` and `java` tasks. `env.blacklist` is a comma seperated list of + environment variable keys not to pass to these tasks. If specified, the + defaults are overriden. The following are the default: + + * `CONSUL_TOKEN` + * `VAULT_TOKEN` + * `ATLAS_TOKEN` + * `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` + * `GOOGLE_APPLICATION_CREDENTIALS` + * `fingerprint.whitelist`: A comma separated list of whitelisted fingerprinters. If specified, fingerprinters not in the whitelist will be disabled. If the whitelist is empty, all fingerprinters are used. From 5352bab6dcc1c833fdc6f0ec334cd589104036c0 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Wed, 23 Mar 2016 14:07:12 -0700 Subject: [PATCH 2/2] Comment and don't override --- client/driver/env/env.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/client/driver/env/env.go b/client/driver/env/env.go index ece460043ee6..c59cb2f87bdd 100644 --- a/client/driver/env/env.go +++ b/client/driver/env/env.go @@ -306,6 +306,9 @@ func (t *TaskEnvironment) AppendEnvvars(m map[string]string) *TaskEnvironment { return t } +// AppendHostEnvvars adds the host environment variables to the tasks. The +// filter parameter can be use to filter host environment from entering the +// tasks. func (t *TaskEnvironment) AppendHostEnvvars(filter []string) *TaskEnvironment { hostEnv := os.Environ() if t.Env == nil { @@ -321,7 +324,14 @@ func (t *TaskEnvironment) AppendHostEnvvars(filter []string) *TaskEnvironment { for _, e := range hostEnv { parts := strings.Split(e, "=") key, value := parts[0], parts[1] - if _, filtered := index[key]; !filtered { + + // Skip filtered environment variables + if _, filtered := index[key]; filtered { + continue + } + + // Don't override the tasks environment variables. + if _, existing := t.Env[key]; !existing { t.Env[key] = value } }