Skip to content

Commit e4347f5

Browse files
EItanyaiplay88keys
andauthored
feat: add token support for kubectl commands (#37)
* feat: add token support for kubectl commands Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io> * use pre-v4 helm version Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io> * Add configuration to disable service token automount Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> * Remove automountServiceAccountToken config Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> * helm config for using default service account Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> * Add tools.k8s.tokenPassthrough for requiring token from auth header Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> * Fix helm version Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> * Remove automountServiceAccountToken from helm test Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> * Redact tokens Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> --------- Signed-off-by: Eitan Yarmush <eitan.yarmush@solo.io> Signed-off-by: Jeremy Alvis <jeremy.alvis@solo.io> Co-authored-by: Jeremy Alvis <jeremy.alvis@solo.io>
1 parent 1f26a89 commit e4347f5

14 files changed

Lines changed: 695 additions & 61 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
- name: Set up Helm
8888
uses: azure/setup-helm@v4.2.0
8989
with:
90-
version: v4.1.0
90+
version: v4.1.1
9191

9292
- name: Install unittest plugin
9393
run: |

helm/kagent-tools/templates/_helpers.tpl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ Allows overriding it for multi-namespace deployments in combined charts.
6565
{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}}
6666
{{- end }}
6767

68+
{{/*
69+
Service account name: default when useDefaultServiceAccount is true, otherwise the chart fullname.
70+
*/}}
71+
{{- define "kagent.serviceAccountName" -}}
72+
{{- if .Values.useDefaultServiceAccount }}default{{- else }}{{ include "kagent.fullname" . }}{{- end }}
73+
{{- end }}
74+
6875
{{/*
6976
Watch namespaces - transforms list of namespaces cached by the controller into comma-separated string
7077
Removes duplicates

helm/kagent-tools/templates/clusterrole.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{- if not .Values.useDefaultServiceAccount }}
12
apiVersion: rbac.authorization.k8s.io/v1
23
kind: ClusterRole
34
metadata:
@@ -26,4 +27,5 @@ rules:
2627
verbs:
2728
- get
2829
- list
29-
- watch
30+
- watch
31+
{{- end }}

helm/kagent-tools/templates/clusterrolebinding.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{- if not .Values.useDefaultServiceAccount }}
12
apiVersion: rbac.authorization.k8s.io/v1
23
kind: ClusterRoleBinding
34
metadata:
@@ -41,4 +42,5 @@ roleRef:
4142
subjects:
4243
- kind: ServiceAccount
4344
name: {{ include "kagent.fullname" . }}
44-
namespace: {{ include "kagent.namespace" . }}
45+
namespace: {{ include "kagent.namespace" . }}
46+
{{- end }}

helm/kagent-tools/templates/deployment.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ spec:
5151

5252
securityContext:
5353
{{- toYaml .Values.podSecurityContext | nindent 8 }}
54-
serviceAccountName: {{ include "kagent.fullname" . }}
54+
serviceAccountName: {{ include "kagent.serviceAccountName" . }}
5555
containers:
5656
- name: tools
5757
command:
@@ -91,6 +91,8 @@ spec:
9191
value: {{ .Values.otel.tracing.exporter.otlp.timeout | quote }}
9292
- name: OTEL_EXPORTER_OTLP_TRACES_INSECURE
9393
value: {{ .Values.otel.tracing.exporter.otlp.insecure | quote }}
94+
- name: TOKEN_PASSTHROUGH
95+
value: {{ (index .Values.tools "k8s" | default dict).tokenPassthrough | default false | quote }}
9496
{{- with .Values.tools.env }}
9597
{{- toYaml . | nindent 12 }}
9698
{{- end }}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
{{- if not .Values.useDefaultServiceAccount }}
12
apiVersion: v1
23
kind: ServiceAccount
34
metadata:
45
name: {{ include "kagent.fullname" . }}
56
namespace: {{ include "kagent.namespace" . }}
67
labels:
7-
{{- include "kagent.labels" . | nindent 4 }}
8+
{{- include "kagent.labels" . | nindent 4 }}
9+
{{- end }}

helm/kagent-tools/tests/deployment_test.yaml

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,46 @@ tests:
6060
path: spec.template.spec.containers[0].resources.limits.memory
6161
value: 512Mi
6262

63-
- it: should have correct service account name
63+
- it: should use default service account when useDefaultServiceAccount is true
6464
template: deployment.yaml
65+
set:
66+
useDefaultServiceAccount: true
67+
asserts:
68+
- equal:
69+
path: spec.template.spec.serviceAccountName
70+
value: default
71+
72+
- it: should use dedicated service account when useDefaultServiceAccount is false
73+
template: deployment.yaml
74+
set:
75+
useDefaultServiceAccount: false
6576
asserts:
6677
- equal:
6778
path: spec.template.spec.serviceAccountName
6879
value: RELEASE-NAME
6980

81+
- it: should set token passthrough env when tools.k8s.tokenPassthrough is true
82+
template: deployment.yaml
83+
set:
84+
tools.k8s.tokenPassthrough: true
85+
asserts:
86+
- contains:
87+
path: spec.template.spec.containers[0].env
88+
content:
89+
name: TOKEN_PASSTHROUGH
90+
value: "true"
91+
92+
- it: should set token passthrough env when tools.k8s.tokenPassthrough is false
93+
template: deployment.yaml
94+
set:
95+
tools.k8s.tokenPassthrough: false
96+
asserts:
97+
- contains:
98+
path: spec.template.spec.containers[0].env
99+
content:
100+
name: TOKEN_PASSTHROUGH
101+
value: "false"
102+
70103
- it: should have correct container port
71104
template: deployment.yaml
72105
asserts:

helm/kagent-tools/values.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Default values for kagent
22
replicaCount: 1
33

4+
# When true: pods use the default service account and no ClusterRole/ClusterRoleBinding are created.
5+
# When false: a dedicated ServiceAccount and RBAC are created.
6+
useDefaultServiceAccount: false
7+
48
global:
59
tag: ""
610

@@ -27,6 +31,10 @@ tools:
2731
limits:
2832
cpu: 1000m
2933
memory: 512Mi
34+
k8s:
35+
# When true: a Bearer token in the Authorization header on each request is passed to kubectl; fails if missing
36+
# When false: kubectl uses in-cluster ServiceAccount.
37+
tokenPassthrough: false
3038
prometheus:
3139
url: "prometheus.kagent.svc.cluster.local:9090"
3240
username: ""

internal/cmd/cmd.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ type DefaultShellExecutor struct{}
2020
func (e *DefaultShellExecutor) Exec(ctx context.Context, command string, args ...string) ([]byte, error) {
2121
log := logger.WithContext(ctx)
2222
startTime := time.Now()
23+
redactedArgs := logger.RedactArgsForLog(args)
2324

2425
log.Info("executing command",
2526
"command", command,
26-
"args", args,
27+
"args", redactedArgs,
2728
)
2829

2930
cmd := exec.CommandContext(ctx, command, args...)
@@ -34,15 +35,15 @@ func (e *DefaultShellExecutor) Exec(ctx context.Context, command string, args ..
3435
if err != nil {
3536
log.Error("command execution failed",
3637
"command", command,
37-
"args", args,
38+
"args", redactedArgs,
3839
"error", err,
3940
"output", string(output),
4041
"duration", duration.Seconds(),
4142
)
4243
} else {
4344
log.Info("command execution successful",
4445
"command", command,
45-
"args", args,
46+
"args", redactedArgs,
4647
"duration", duration.Seconds(),
4748
)
4849
}

internal/commands/builder.go

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type CommandBuilder struct {
2929
namespace string
3030
context string
3131
kubeconfig string
32+
token string
3233
output string
3334
labels map[string]string
3435
annotations map[string]string
@@ -120,6 +121,14 @@ func (cb *CommandBuilder) WithKubeconfig(kubeconfig string) *CommandBuilder {
120121
return cb
121122
}
122123

124+
// WithToken sets the authentication token for kubectl commands
125+
func (cb *CommandBuilder) WithToken(token string) *CommandBuilder {
126+
if token != "" {
127+
cb.token = token
128+
}
129+
return cb
130+
}
131+
123132
// WithOutput sets the output format
124133
func (cb *CommandBuilder) WithOutput(output string) *CommandBuilder {
125134
validOutputs := []string{"json", "yaml", "wide", "name", "custom-columns", "custom-columns-file", "go-template", "go-template-file", "jsonpath", "jsonpath-file"}
@@ -240,6 +249,11 @@ func (cb *CommandBuilder) Build() (string, []string, error) {
240249
args = append(args, "--kubeconfig", cb.kubeconfig)
241250
}
242251

252+
// Add token if specified
253+
if cb.token != "" {
254+
args = append(args, "--token", cb.token)
255+
}
256+
243257
// Add output format
244258
if cb.output != "" {
245259
args = append(args, "--output", cb.output)
@@ -293,7 +307,7 @@ func (cb *CommandBuilder) Execute(ctx context.Context) (string, error) {
293307
log := logger.WithContext(ctx)
294308
_, span := telemetry.StartSpan(ctx, "commands.execute",
295309
attribute.String("command", cb.command),
296-
attribute.StringSlice("args", cb.args),
310+
attribute.StringSlice("args", logger.RedactArgsForLog(cb.args)),
297311
attribute.Bool("cached", cb.cached),
298312
)
299313
defer span.End()
@@ -308,14 +322,15 @@ func (cb *CommandBuilder) Execute(ctx context.Context) (string, error) {
308322
return "", err
309323
}
310324

325+
redactedArgs := logger.RedactArgsForLog(args)
311326
span.SetAttributes(
312327
attribute.String("built_command", command),
313-
attribute.StringSlice("built_args", args),
328+
attribute.StringSlice("built_args", redactedArgs),
314329
)
315330

316331
log.Debug("executing command",
317332
"command", command,
318-
"args", args,
333+
"args", redactedArgs,
319334
"cached", cb.cached,
320335
)
321336

@@ -343,9 +358,10 @@ func (cb *CommandBuilder) Execute(ctx context.Context) (string, error) {
343358

344359
func (cb *CommandBuilder) executeWithCache(ctx context.Context, command string, args []string) (string, error) {
345360
log := logger.WithContext(ctx)
361+
redactedArgs := logger.RedactArgsForLog(args)
346362
_, span := telemetry.StartSpan(ctx, "commands.executeWithCache",
347363
attribute.String("command", command),
348-
attribute.StringSlice("args", args),
364+
attribute.StringSlice("args", redactedArgs),
349365
attribute.Bool("cached", true),
350366
)
351367
defer span.End()
@@ -357,7 +373,7 @@ func (cb *CommandBuilder) executeWithCache(ctx context.Context, command string,
357373

358374
log.Info("executing cached command",
359375
"command", command,
360-
"args", args,
376+
"args", redactedArgs,
361377
"cache_key", cacheKey,
362378
"cache_ttl", cb.cacheTTL.String(),
363379
)
@@ -374,7 +390,7 @@ func (cb *CommandBuilder) executeWithCache(ctx context.Context, command string,
374390
telemetry.AddEvent(span, "cache.miss.executing_command")
375391
log.Debug("cache miss, executing command",
376392
"command", command,
377-
"args", args,
393+
"args", redactedArgs,
378394
)
379395
return cb.executeCommand(ctx, command, args)
380396
})
@@ -383,7 +399,7 @@ func (cb *CommandBuilder) executeWithCache(ctx context.Context, command string,
383399
telemetry.RecordError(span, err, "Cached command execution failed")
384400
log.Error("cached command execution failed",
385401
"command", command,
386-
"args", args,
402+
"args", redactedArgs,
387403
"cache_key", cacheKey,
388404
"error", err,
389405
)
@@ -393,7 +409,7 @@ func (cb *CommandBuilder) executeWithCache(ctx context.Context, command string,
393409
telemetry.RecordSuccess(span, "Cached command executed successfully")
394410
log.Info("cached command execution successful",
395411
"command", command,
396-
"args", args,
412+
"args", redactedArgs,
397413
"cache_key", cacheKey,
398414
"result_length", len(result),
399415
)

0 commit comments

Comments
 (0)