Skip to content

Commit fb0187c

Browse files
authored
Merge pull request #129603 from aravindhp/automated-cherry-pick-of-#129599-upstream-release-1.29
Automated cherry pick of #129599: Automated cherry pick of #129595: kubelet: use env vars in node log query PS command
2 parents 86e25a0 + 1e45808 commit fb0187c

9 files changed

+142
-37
lines changed

pkg/features/kube_features.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,8 @@ const (
586586
// kep: http://kep.k8s.io/2271
587587
// alpha: v1.27
588588
//
589-
// Enables querying logs of node services using the /logs endpoint
589+
// Enables querying logs of node services using the /logs endpoint. Enabling this feature has security implications.
590+
// The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise.
590591
NodeLogQuery featuregate.Feature = "NodeLogQuery"
591592

592593
// owner: @xing-yang @sonasingh46

pkg/generated/openapi/zz_generated.openapi.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/kubelet/apis/config/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ type KubeletConfiguration struct {
398398
EnableSystemLogHandler bool
399399
// EnableSystemLogQuery enables the node log query feature on the /logs endpoint.
400400
// EnableSystemLogHandler has to be enabled in addition for this feature to work.
401+
// Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging
402+
// purposes and disabling otherwise.
401403
// +featureGate=NodeLogQuery
402404
// +optional
403405
EnableSystemLogQuery bool

pkg/kubelet/kubelet_server_journal.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -316,14 +316,15 @@ func (n *nodeLogQuery) splitNativeVsFileLoggers(ctx context.Context) ([]string,
316316
// copyServiceLogs invokes journalctl or Get-WinEvent with the provided args. Note that
317317
// services are explicitly passed here to account for the heuristics.
318318
func (n *nodeLogQuery) copyServiceLogs(ctx context.Context, w io.Writer, services []string, previousBoot int) {
319-
cmdStr, args, err := getLoggingCmd(n, services)
319+
cmdStr, args, cmdEnv, err := getLoggingCmd(n, services)
320320
if err != nil {
321321
fmt.Fprintf(w, "\nfailed to get logging cmd: %v\n", err)
322322
return
323323
}
324324
cmd := exec.CommandContext(ctx, cmdStr, args...)
325325
cmd.Stdout = w
326326
cmd.Stderr = w
327+
cmd.Env = append(os.Environ(), cmdEnv...)
327328

328329
if err := cmd.Run(); err != nil {
329330
if _, ok := err.(*exec.ExitError); ok {

pkg/kubelet/kubelet_server_journal_linux.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@ import (
2626
)
2727

2828
// getLoggingCmd returns the journalctl cmd and arguments for the given nodeLogQuery and boot. Note that
29-
// services are explicitly passed here to account for the heuristics
30-
func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) {
31-
args := []string{
29+
// services are explicitly passed here to account for the heuristics.
30+
// The return values are:
31+
// - cmd: the command to be executed
32+
// - args: arguments to the command
33+
// - cmdEnv: environment variables when the command will be executed
34+
func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) {
35+
args = []string{
3236
"--utc",
3337
"--no-pager",
3438
"--output=short-precise",
@@ -55,7 +59,7 @@ func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error)
5559
args = append(args, "--boot", fmt.Sprintf("%d", *n.Boot))
5660
}
5761

58-
return "journalctl", args, nil
62+
return "journalctl", args, nil, nil
5963
}
6064

6165
// checkForNativeLogger checks journalctl output for a service

pkg/kubelet/kubelet_server_journal_others.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import (
2424
)
2525

2626
// getLoggingCmd on unsupported operating systems returns the echo command and a warning message (as strings)
27-
func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) {
28-
return "", []string{}, errors.New("Operating System Not Supported")
27+
func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) {
28+
return "", args, cmdEnv, errors.New("Operating System Not Supported")
2929
}
3030

3131
// checkForNativeLogger on unsupported operating systems returns false

pkg/kubelet/kubelet_server_journal_test.go

+40-9
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,62 @@ import (
3030
)
3131

3232
func Test_getLoggingCmd(t *testing.T) {
33+
var emptyCmdEnv []string
3334
tests := []struct {
34-
name string
35-
args nodeLogQuery
36-
wantLinux []string
37-
wantWindows []string
38-
wantOtherOS []string
35+
name string
36+
args nodeLogQuery
37+
services []string
38+
wantLinux []string
39+
wantWindows []string
40+
wantLinuxCmdEnv []string
41+
wantWindowsCmdEnv []string
3942
}{
4043
{
41-
args: nodeLogQuery{},
42-
wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"},
43-
wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"},
44+
name: "basic",
45+
args: nodeLogQuery{},
46+
services: []string{},
47+
wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"},
48+
wantLinuxCmdEnv: emptyCmdEnv,
49+
wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"},
50+
wantWindowsCmdEnv: emptyCmdEnv,
51+
},
52+
{
53+
name: "two providers",
54+
args: nodeLogQuery{},
55+
services: []string{"p1", "p2"},
56+
wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"},
57+
wantLinuxCmdEnv: emptyCmdEnv,
58+
wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName=$Env:kubelet_provider0,$Env:kubelet_provider1} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"},
59+
wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider1=p2"},
60+
},
61+
{
62+
name: "empty provider",
63+
args: nodeLogQuery{},
64+
services: []string{"p1", "", "p2"},
65+
wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"},
66+
wantLinuxCmdEnv: emptyCmdEnv,
67+
wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName=$Env:kubelet_provider0,$Env:kubelet_provider2} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"},
68+
wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider2=p2"},
4469
},
4570
}
4671
for _, tt := range tests {
4772
t.Run(tt.name, func(t *testing.T) {
48-
_, got, err := getLoggingCmd(&tt.args, []string{})
73+
_, got, gotCmdEnv, err := getLoggingCmd(&tt.args, tt.services)
4974
switch os := runtime.GOOS; os {
5075
case "linux":
5176
if !reflect.DeepEqual(got, tt.wantLinux) {
5277
t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantLinux)
5378
}
79+
if !reflect.DeepEqual(gotCmdEnv, tt.wantLinuxCmdEnv) {
80+
t.Errorf("gotCmdEnv %v, wantLinuxCmdEnv %v", gotCmdEnv, tt.wantLinuxCmdEnv)
81+
}
5482
case "windows":
5583
if !reflect.DeepEqual(got, tt.wantWindows) {
5684
t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantWindows)
5785
}
86+
if !reflect.DeepEqual(gotCmdEnv, tt.wantWindowsCmdEnv) {
87+
t.Errorf("gotCmdEnv %v, wantWindowsCmdEnv %v", gotCmdEnv, tt.wantWindowsCmdEnv)
88+
}
5889
default:
5990
if err == nil {
6091
t.Errorf("getLoggingCmd() = %v, want err", got)

pkg/kubelet/kubelet_server_journal_windows.go

+83-19
Original file line numberDiff line numberDiff line change
@@ -27,43 +27,107 @@ import (
2727

2828
const powershellExe = "PowerShell.exe"
2929

30-
// getLoggingCmd returns the powershell cmd and arguments for the given nodeLogQuery and boot
31-
func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) {
32-
args := []string{
30+
// getLoggingCmd returns the powershell cmd, arguments, and environment variables for the given nodeLogQuery and boot.
31+
// All string inputs are environment variables to stop subcommands expressions from being executed.
32+
// The return values are:
33+
// - cmd: the command to be executed
34+
// - args: arguments to the command
35+
// - cmdEnv: environment variables when the command will be executed
36+
func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) {
37+
cmdEnv = getLoggingCmdEnv(n, services)
38+
39+
var includeSinceTime, includeUntilTime, includeTailLines, includePattern bool
40+
if n.SinceTime != nil {
41+
includeSinceTime = true
42+
}
43+
if n.UntilTime != nil {
44+
includeUntilTime = true
45+
}
46+
if n.TailLines != nil {
47+
includeTailLines = true
48+
}
49+
if len(n.Pattern) > 0 {
50+
includePattern = true
51+
}
52+
53+
var includeServices []bool
54+
for _, service := range services {
55+
includeServices = append(includeServices, len(service) > 0)
56+
}
57+
58+
args = getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern, includeServices)
59+
60+
return powershellExe, args, cmdEnv, nil
61+
}
62+
63+
// getLoggingCmdArgs returns arguments that need to be passed to powershellExe
64+
func getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern bool, services []bool) (args []string) {
65+
args = []string{
3366
"-NonInteractive",
3467
"-ExecutionPolicy", "Bypass",
3568
"-Command",
3669
}
3770

38-
psCmd := "Get-WinEvent -FilterHashtable @{LogName='Application'"
39-
if n.SinceTime != nil {
40-
psCmd += fmt.Sprintf("; StartTime='%s'", n.SinceTime.Format(dateLayout))
71+
psCmd := `Get-WinEvent -FilterHashtable @{LogName='Application'`
72+
73+
if includeSinceTime {
74+
psCmd += fmt.Sprintf(`; StartTime="$Env:kubelet_sinceTime"`)
4175
}
42-
if n.UntilTime != nil {
43-
psCmd += fmt.Sprintf("; EndTime='%s'", n.UntilTime.Format(dateLayout))
76+
if includeUntilTime {
77+
psCmd += fmt.Sprintf(`; EndTime="$Env:kubelet_untilTime"`)
4478
}
79+
4580
var providers []string
46-
for _, service := range services {
47-
if len(service) > 0 {
48-
providers = append(providers, "'"+service+"'")
81+
for i := range services {
82+
if services[i] {
83+
providers = append(providers, fmt.Sprintf("$Env:kubelet_provider%d", i))
4984
}
5085
}
86+
5187
if len(providers) > 0 {
5288
psCmd += fmt.Sprintf("; ProviderName=%s", strings.Join(providers, ","))
5389
}
54-
psCmd += "}"
55-
if n.TailLines != nil {
56-
psCmd += fmt.Sprintf(" -MaxEvents %d", *n.TailLines)
90+
91+
psCmd += `}`
92+
if includeTailLines {
93+
psCmd += fmt.Sprint(` -MaxEvents $Env:kubelet_tailLines`)
5794
}
58-
psCmd += " | Sort-Object TimeCreated"
59-
if len(n.Pattern) > 0 {
60-
psCmd += fmt.Sprintf(" | Where-Object -Property Message -Match '%s'", n.Pattern)
95+
psCmd += ` | Sort-Object TimeCreated`
96+
97+
if includePattern {
98+
psCmd += fmt.Sprintf(` | Where-Object -Property Message -Match "$Env:kubelet_pattern"`)
6199
}
62-
psCmd += " | Format-Table -AutoSize -Wrap"
100+
psCmd += ` | Format-Table -AutoSize -Wrap`
63101

64102
args = append(args, psCmd)
65103

66-
return powershellExe, args, nil
104+
return args
105+
}
106+
107+
// getLoggingCmdEnv returns the environment variables that will be present when powershellExe is executed
108+
func getLoggingCmdEnv(n *nodeLogQuery, services []string) (cmdEnv []string) {
109+
if n.SinceTime != nil {
110+
cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_sinceTime=%s", n.SinceTime.Format(dateLayout)))
111+
}
112+
if n.UntilTime != nil {
113+
cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_untilTime=%s", n.UntilTime.Format(dateLayout)))
114+
}
115+
116+
for i, service := range services {
117+
if len(service) > 0 {
118+
cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_provider%d=%s", i, service))
119+
}
120+
}
121+
122+
if n.TailLines != nil {
123+
cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_tailLines=%d", *n.TailLines))
124+
}
125+
126+
if len(n.Pattern) > 0 {
127+
cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_pattern=%s", n.Pattern))
128+
}
129+
130+
return cmdEnv
67131
}
68132

69133
// checkForNativeLogger always returns true for Windows

staging/src/k8s.io/kubelet/config/v1beta1/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,8 @@ type KubeletConfiguration struct {
699699
EnableSystemLogHandler *bool `json:"enableSystemLogHandler,omitempty"`
700700
// enableSystemLogQuery enables the node log query feature on the /logs endpoint.
701701
// EnableSystemLogHandler has to be enabled in addition for this feature to work.
702+
// Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging
703+
// purposes and disabling otherwise.
702704
// Default: false
703705
// +featureGate=NodeLogQuery
704706
// +optional

0 commit comments

Comments
 (0)