Skip to content

Commit b69d383

Browse files
committed
provisioner: don't pass CODER_ variables
1 parent 0a5e554 commit b69d383

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

provisioner/terraform/executor.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@ func (e executor) basicEnv() []string {
4141
return env
4242
}
4343

44+
// stripCoderEnv removes CODER_ environment variables to prevent accidentally
45+
// passing in secrets. See https://github.com/coder/coder/issues/4635.
46+
func stripCoderEnv(env []string) []string {
47+
strippedEnv := make([]string, 0, len(env))
48+
for _, e := range env {
49+
if strings.HasPrefix(e, "CODER_") {
50+
continue
51+
}
52+
strippedEnv = append(strippedEnv, e)
53+
}
54+
return strippedEnv
55+
}
56+
4457
func (e executor) execWriteOutput(ctx, killCtx context.Context, args, env []string, stdOutWriter, stdErrWriter io.WriteCloser) (err error) {
4558
defer func() {
4659
closeErr := stdOutWriter.Close()
@@ -59,7 +72,7 @@ func (e executor) execWriteOutput(ctx, killCtx context.Context, args, env []stri
5972
// #nosec
6073
cmd := exec.CommandContext(killCtx, e.binaryPath, args...)
6174
cmd.Dir = e.workdir
62-
cmd.Env = env
75+
cmd.Env = stripCoderEnv(env)
6376

6477
// We want logs to be written in the correct order, so we wrap all logging
6578
// in a sync.Mutex.

provisioner/terraform/provision_test.go

+63-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ func TestProvision(t *testing.T) {
415415
// nolint:paralleltest
416416
func TestProvision_ExtraEnv(t *testing.T) {
417417
// #nosec
418-
secretValue := "oinae3uinxase"
418+
const secretValue = "oinae3uinxase"
419419
t.Setenv("TF_LOG", "INFO")
420420
t.Setenv("TF_SUPERSECRET", secretValue)
421421

@@ -459,3 +459,65 @@ func TestProvision_ExtraEnv(t *testing.T) {
459459
}
460460
require.True(t, found)
461461
}
462+
463+
// nolint:paralleltest
464+
func TestProvision_SafeEnv(t *testing.T) {
465+
// #nosec
466+
const (
467+
passedValue = "superautopets"
468+
secretValue = "oinae3uinxase"
469+
)
470+
t.Setenv("SOME_ENV", passedValue)
471+
// We must ensure CODER_ variables aren't passed through to avoid leaking
472+
// control plane secrets (e.g. PG URL).
473+
t.Setenv("CODER_SECRET", secretValue)
474+
475+
const echoResource = `
476+
resource "null_resource" "a" {
477+
provisioner "local-exec" {
478+
command = "env"
479+
}
480+
}
481+
482+
`
483+
484+
ctx, api := setupProvisioner(t, nil)
485+
486+
directory := t.TempDir()
487+
path := filepath.Join(directory, "main.tf")
488+
err := os.WriteFile(path, []byte(echoResource), 0o600)
489+
require.NoError(t, err)
490+
491+
request := &proto.Provision_Request{
492+
Type: &proto.Provision_Request_Start{
493+
Start: &proto.Provision_Start{
494+
Directory: directory,
495+
Metadata: &proto.Provision_Metadata{
496+
WorkspaceTransition: proto.WorkspaceTransition_START,
497+
},
498+
},
499+
},
500+
}
501+
response, err := api.Provision(ctx)
502+
require.NoError(t, err)
503+
err = response.Send(request)
504+
require.NoError(t, err)
505+
found := false
506+
for {
507+
msg, err := response.Recv()
508+
require.NoError(t, err)
509+
510+
if log := msg.GetLog(); log != nil {
511+
t.Log(log.Level.String(), log.Output)
512+
if strings.Contains(log.Output, passedValue) {
513+
found = true
514+
}
515+
require.NotContains(t, log.Output, secretValue)
516+
}
517+
if c := msg.GetComplete(); c != nil {
518+
require.Empty(t, c.Error)
519+
break
520+
}
521+
}
522+
require.True(t, found)
523+
}

0 commit comments

Comments
 (0)