diff --git a/client/config/config.go b/client/config/config.go index ac0968eff1f..702d9a5e28d 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 447836ec19c..c59cb2f87bd 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,39 @@ 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 { + 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] + + // 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 + } + } + + 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 de2d6805d89..f2dcbccfa54 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 672f0e96cb0..2e898b8f195 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 d4863df57c9..eaa2a01d5bd 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 2dc0aceebbd..4143cb4dfc7 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 2590defe0fe..9ceed03fe95 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.