From f3d09755c59d5b800c8eb8047b2b23dbf6b7cea3 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 ac0968eff..702d9a5e2 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 447836ec1..ece460043 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 de2d6805d..f2dcbccfa 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 672f0e96c..2e898b8f1 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 d4863df57..eaa2a01d5 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 2dc0aceeb..4143cb4df 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 2590defe0..9ceed03fe 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 5eddb939f3264de71476a2d7e230b31e4b841d3b 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 ece460043..c59cb2f87 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 } }