Skip to content

Commit 2bf2584

Browse files
authored
Merge pull request #280 from buildkite/PDP-2081-redaction-config
Add redacted-vars config
2 parents 97640fd + c5a057c commit 2bf2584

File tree

7 files changed

+89
-22
lines changed

7 files changed

+89
-22
lines changed

charts/agent-stack-k8s/values.schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,15 @@
204204
"default": "",
205205
"title": "The UUID of the Buildkite cluster to pull Jobs from",
206206
"examples": [""]
207+
},
208+
"additional-redacted-vars": {
209+
"type": "array",
210+
"default": [],
211+
"title": "Additional environment variables to redact values from logs",
212+
"items": {
213+
"type": "string"
214+
},
215+
"examples": [["SECRET_RECIPE"]]
207216
}
208217
},
209218
"examples": [

internal/controller/config/config.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@ const (
1515
)
1616

1717
type Config struct {
18-
Debug bool `mapstructure:"debug"`
19-
AgentTokenSecret string `mapstructure:"agent-token-secret" validate:"required"`
20-
BuildkiteToken string `mapstructure:"buildkite-token" validate:"required"`
21-
Image string `mapstructure:"image" validate:"required"`
22-
JobTTL time.Duration `mapstructure:"job-ttl"`
23-
MaxInFlight int `mapstructure:"max-in-flight" validate:"min=0"`
24-
Namespace string `mapstructure:"namespace" validate:"required"`
25-
Org string `mapstructure:"org" validate:"required"`
26-
Tags stringSlice `mapstructure:"tags" validate:"min=1"`
27-
ProfilerAddress string `mapstructure:"profiler-address" validate:"omitempty,hostname_port"`
28-
ClusterUUID string `mapstructure:"cluster-uuid" validate:"omitempty"`
18+
Debug bool `mapstructure:"debug"`
19+
AgentTokenSecret string `mapstructure:"agent-token-secret" validate:"required"`
20+
BuildkiteToken string `mapstructure:"buildkite-token" validate:"required"`
21+
Image string `mapstructure:"image" validate:"required"`
22+
JobTTL time.Duration `mapstructure:"job-ttl"`
23+
MaxInFlight int `mapstructure:"max-in-flight" validate:"min=0"`
24+
Namespace string `mapstructure:"namespace" validate:"required"`
25+
Org string `mapstructure:"org" validate:"required"`
26+
Tags stringSlice `mapstructure:"tags" validate:"min=1"`
27+
ProfilerAddress string `mapstructure:"profiler-address" validate:"omitempty,hostname_port"`
28+
ClusterUUID string `mapstructure:"cluster-uuid" validate:"omitempty"`
29+
AdditionalRedactedVars stringSlice `mapstructure:"additional-redacted-vars" validate:"omitempty"`
2930
}
3031

3132
type stringSlice []string
@@ -47,5 +48,8 @@ func (c Config) MarshalLogObject(enc zapcore.ObjectEncoder) error {
4748
enc.AddString("org", c.Org)
4849
enc.AddString("profiler-address", c.ProfilerAddress)
4950
enc.AddString("cluster-uuid", c.ClusterUUID)
51+
if err := enc.AddArray("additional-redacted-vars", c.AdditionalRedactedVars); err != nil {
52+
return err
53+
}
5054
return enc.AddArray("tags", c.Tags)
5155
}

internal/controller/controller.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ func Run(
4949
}
5050

5151
sched := scheduler.New(logger.Named("scheduler"), k8sClient, scheduler.Config{
52-
Namespace: cfg.Namespace,
53-
Image: cfg.Image,
54-
AgentToken: cfg.AgentTokenSecret,
55-
JobTTL: cfg.JobTTL,
52+
Namespace: cfg.Namespace,
53+
Image: cfg.Image,
54+
AgentToken: cfg.AgentTokenSecret,
55+
JobTTL: cfg.JobTTL,
56+
AdditionalRedactedVars: cfg.AdditionalRedactedVars,
5657
})
5758
limiter := scheduler.NewLimiter(logger.Named("limiter"), sched, cfg.MaxInFlight)
5859

internal/controller/scheduler/scheduler.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ const (
3030
)
3131

3232
type Config struct {
33-
Namespace string
34-
Image string
35-
AgentToken string
36-
JobTTL time.Duration
33+
Namespace string
34+
Image string
35+
AgentToken string
36+
JobTTL time.Duration
37+
AdditionalRedactedVars []string
3738
}
3839

3940
func New(logger *zap.Logger, client kubernetes.Interface, cfg Config) *worker {
@@ -228,6 +229,8 @@ func (w *jobWrapper) Build(skipCheckout bool) (*batchv1.Job, error) {
228229
}
229230
}
230231

232+
redactedVars := append(w.cfg.AdditionalRedactedVars, clicommand.RedactedVars.Value.Value()...)
233+
231234
volumeMounts := []corev1.VolumeMount{{Name: "workspace", MountPath: "/workspace"}}
232235
volumeMounts = append(volumeMounts, w.k8sPlugin.ExtraVolumeMounts...)
233236

@@ -277,7 +280,7 @@ func (w *jobWrapper) Build(skipCheckout bool) (*batchv1.Job, error) {
277280
},
278281
corev1.EnvVar{
279282
Name: clicommand.RedactedVars.EnvVar,
280-
Value: strings.Join(clicommand.RedactedVars.Value.Value(), ","),
283+
Value: strings.Join(redactedVars, ","),
281284
},
282285
corev1.EnvVar{
283286
Name: "BUILDKITE_SHELL",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
steps:
2+
- label: ":earth_asia:"
3+
agents:
4+
queue: {{.queue}}
5+
env:
6+
BUILDKITE_SHELL: /bin/sh -ec
7+
# Note that this is example is a bit contrived, since environment
8+
# variables and values can usually be browsed from the Buildkite UI.
9+
ELEVEN_HERBS_AND_SPICES: white pepper and 10 others
10+
plugins:
11+
- kubernetes:
12+
podSpec:
13+
containers:
14+
- image: alpine:latest
15+
command:
16+
- echo
17+
args:
18+
- >-
19+
This should be redacted:
20+
$${ELEVEN_HERBS_AND_SPICES}

internal/integration/integration_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"time"
88

99
"github.com/buildkite/agent-stack-k8s/v2/api"
10+
"github.com/stretchr/testify/assert"
1011
"github.com/stretchr/testify/require"
1112
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
)
@@ -53,6 +54,29 @@ func TestControllerPicksUpJobsWithSubsetOfAgentTags(t *testing.T) {
5354
tc.AssertSuccess(ctx, build)
5455
}
5556

57+
func TestControllerSetsAdditionalRedactedVars(t *testing.T) {
58+
tc := testcase{
59+
T: t,
60+
Fixture: "redacted-vars.yaml",
61+
Repo: repoHTTP,
62+
GraphQL: api.NewClient(cfg.BuildkiteToken),
63+
}.Init()
64+
65+
ctx := context.Background()
66+
pipelineID, cleanup := tc.CreatePipeline(ctx)
67+
t.Cleanup(cleanup)
68+
69+
cfg := cfg
70+
cfg.AdditionalRedactedVars = []string{"ELEVEN_HERBS_AND_SPICES"}
71+
72+
tc.StartController(ctx, cfg)
73+
build := tc.TriggerBuild(ctx, pipelineID)
74+
tc.AssertSuccess(ctx, build)
75+
logs := tc.FetchLogs(build)
76+
assert.Contains(t, logs, "This should be redacted:")
77+
assert.NotContains(t, logs, "white pepper and 10 others")
78+
}
79+
5680
func TestChown(t *testing.T) {
5781
tc := testcase{
5882
T: t,

internal/integration/testcase_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func (t testcase) AssertSuccess(ctx context.Context, build api.Build) {
158158
require.Equal(t, api.BuildStatesPassed, t.waitForBuild(ctx, build))
159159
}
160160

161-
func (t testcase) AssertLogsContain(build api.Build, content string) {
161+
func (t testcase) FetchLogs(build api.Build) string {
162162
t.Helper()
163163

164164
config, err := buildkite.NewTokenConfig(cfg.BuildkiteToken, false)
@@ -187,7 +187,13 @@ func (t testcase) AssertLogsContain(build api.Build, content string) {
187187
assert.NoError(t, err)
188188
}
189189

190-
assert.Contains(t, logs.String(), content)
190+
return logs.String()
191+
}
192+
193+
func (t testcase) AssertLogsContain(build api.Build, content string) {
194+
t.Helper()
195+
196+
assert.Contains(t, t.FetchLogs(build), content)
191197
}
192198

193199
func (t testcase) AssertArtifactsContain(build api.Build, expected ...string) {

0 commit comments

Comments
 (0)