Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d49d57e

Browse files
authored
provisioner: don't pass CODER_ variables (coder#4638)
1 parent 12cb4f1 commit d49d57e

File tree

7 files changed

+139
-123
lines changed

7 files changed

+139
-123
lines changed

provisioner/terraform/executor.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type executor struct {
3232
func (e executor) basicEnv() []string {
3333
// Required for "terraform init" to find "git" to
3434
// clone Terraform modules.
35-
env := os.Environ()
35+
env := safeEnviron()
3636
// Only Linux reliably works with the Terraform plugin
3737
// cache directory. It's unknown why this is.
3838
if e.cachePath != "" && runtime.GOOS == "linux" {
@@ -56,6 +56,10 @@ func (e executor) execWriteOutput(ctx, killCtx context.Context, args, env []stri
5656
return ctx.Err()
5757
}
5858

59+
if isCanarySet(env) {
60+
return xerrors.New("environment variables not sanitized, this is a bug within Coder")
61+
}
62+
5963
// #nosec
6064
cmd := exec.CommandContext(killCtx, e.binaryPath, args...)
6165
cmd.Dir = e.workdir

provisioner/terraform/provision.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ func provisionVars(start *proto.Provision_Start) ([]string, error) {
188188
}
189189

190190
func provisionEnv(start *proto.Provision_Start) ([]string, error) {
191-
env := os.Environ()
191+
env := safeEnviron()
192192
env = append(env,
193193
"CODER_AGENT_URL="+start.Metadata.CoderUrl,
194194
"CODER_WORKSPACE_TRANSITION="+strings.ToLower(start.Metadata.WorkspaceTransition.String()),
@@ -232,12 +232,12 @@ var (
232232
)
233233

234234
func logTerraformEnvVars(logr logger) error {
235-
env := os.Environ()
235+
env := safeEnviron()
236236
for _, e := range env {
237237
if strings.HasPrefix(e, "TF_") {
238238
parts := strings.SplitN(e, "=", 2)
239239
if len(parts) != 2 {
240-
panic("os.Environ() returned vars not in key=value form")
240+
panic("safeEnviron() returned vars not in key=value form")
241241
}
242242
if !tfEnvSafeToPrint[parts[0]] {
243243
parts[1] = "<value redacted>"

provisioner/terraform/provision_test.go

+74-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,76 @@ 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+
471+
t.Setenv("VALID_USER_ENV", passedValue)
472+
473+
// We ensure random CODER_ variables aren't passed through to avoid leaking
474+
// control plane secrets (e.g. PG URL).
475+
t.Setenv("CODER_SECRET", secretValue)
476+
477+
const echoResource = `
478+
resource "null_resource" "a" {
479+
provisioner "local-exec" {
480+
command = "env"
481+
}
482+
}
483+
484+
`
485+
486+
ctx, api := setupProvisioner(t, nil)
487+
488+
directory := t.TempDir()
489+
path := filepath.Join(directory, "main.tf")
490+
err := os.WriteFile(path, []byte(echoResource), 0o600)
491+
require.NoError(t, err)
492+
493+
request := &proto.Provision_Request{
494+
Type: &proto.Provision_Request_Start{
495+
Start: &proto.Provision_Start{
496+
Directory: directory,
497+
Metadata: &proto.Provision_Metadata{
498+
WorkspaceTransition: proto.WorkspaceTransition_START,
499+
},
500+
},
501+
},
502+
}
503+
response, err := api.Provision(ctx)
504+
require.NoError(t, err)
505+
err = response.Send(request)
506+
require.NoError(t, err)
507+
var (
508+
foundUserEnv = false
509+
// Some CODER_ environment variables used by our Terraform provider
510+
// must make it through.
511+
foundCoderEnv = false
512+
)
513+
for {
514+
msg, err := response.Recv()
515+
require.NoError(t, err)
516+
517+
if log := msg.GetLog(); log != nil {
518+
t.Log(log.Level.String(), log.Output)
519+
if strings.Contains(log.Output, passedValue) {
520+
foundUserEnv = true
521+
}
522+
if strings.Contains(log.Output, "CODER_") {
523+
foundCoderEnv = true
524+
}
525+
require.NotContains(t, log.Output, secretValue)
526+
}
527+
if c := msg.GetComplete(); c != nil {
528+
require.Empty(t, c.Error)
529+
break
530+
}
531+
}
532+
require.True(t, foundUserEnv)
533+
require.True(t, foundCoderEnv)
534+
}

provisioner/terraform/safeenv.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package terraform
2+
3+
import (
4+
"os"
5+
"strings"
6+
)
7+
8+
// We must clean CODER_ environment variables to avoid accidentally passing in
9+
// secrets like the Postgres connection string. See
10+
// https://github.com/coder/coder/issues/4635.
11+
//
12+
// safeEnviron() is provided as an os.Environ() alternative that strips CODER_
13+
// variables. As an additional precaution, we check a canary variable before
14+
// provisioner exec.
15+
//
16+
// We cannot strip all CODER_ variables at exec because some are used to
17+
// configure the provisioner.
18+
19+
const unsafeEnvCanary = "CODER_DONT_PASS"
20+
21+
func init() {
22+
os.Setenv(unsafeEnvCanary, "true")
23+
}
24+
25+
func envName(env string) string {
26+
parts := strings.SplitN(env, "=", 1)
27+
if len(parts) > 0 {
28+
return parts[0]
29+
}
30+
return ""
31+
}
32+
33+
func isCanarySet(env []string) bool {
34+
for _, e := range env {
35+
if envName(e) == unsafeEnvCanary {
36+
return true
37+
}
38+
}
39+
return false
40+
}
41+
42+
// safeEnviron wraps os.Environ but removes CODER_ environment variables.
43+
func safeEnviron() []string {
44+
env := os.Environ()
45+
strippedEnv := make([]string, 0, len(env))
46+
47+
for _, e := range env {
48+
name := envName(e)
49+
if strings.HasPrefix(name, "CODER_") {
50+
continue
51+
}
52+
strippedEnv = append(strippedEnv, e)
53+
}
54+
return strippedEnv
55+
}

site/src/components/Resources/AgentLatency.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ export const AgentLatency: FC<{ agent: WorkspaceAgent }> = ({ agent }) => {
6868
>
6969
<HelpTooltipTitle>Latency</HelpTooltipTitle>
7070
<HelpTooltipText>
71-
Latency from relay servers, used when connections cannot connect
72-
peer-to-peer. Star indicates the preferred relay.
71+
This is the latency overhead on non peer to peer connections. The star
72+
indicates the preferred relay.
7373
</HelpTooltipText>
7474

7575
<HelpTooltipText>

site/src/components/Resources/ResourceAgentLatency.stories.tsx

-46
This file was deleted.

site/src/components/Resources/ResourceAgentLatency.tsx

-70
This file was deleted.

0 commit comments

Comments
 (0)