CVE-2024-9042
Description
This CVE affects only Windows worker nodes. Your worker node is vulnerable to this issue if it is running one of the affected versions listed below.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A vulnerability in Kubernetes Windows worker nodes (CVE-2024-9042) allows privilege escalation via improper handling of environment variables in the node log query feature, leading to potential container breakout.
Vulnerability
Description CVE-2024-9042 is a medium-severity vulnerability affecting Kubernetes Windows worker nodes (CVSS 5.9). The root cause lies in the getLoggingCmd function used for querying logs on Windows nodes. The official description states it only impacts Windows worker nodes running affected versions of Kubernetes [1]. The commit diffs show that the previous implementation did not properly separate environment variables for the PowerShell command used to fetch Windows event logs [2][3][4]. Specifically, when multiple service providers are specified, the environment variables (like $Env:kubelet_provider0) could inadvertently be injected into the command string without proper sanitization, potentially allowing an attacker to control the command execution context.
Exploitation
An attacker with the ability to specify service provider names (e.g., via configuration or API) on a Windows node could craft a malicious provider name containing shell metacharacters. Because the environment variables are passed directly into the PowerShell command without adequate escaping, the attacker could inject arbitrary PowerShell commands. This attack does not require prior authentication on the node but does require the ability to influence the node's logging configuration, which might be achieved through Kubernetes API calls or by exploiting other misconfigurations [1][2].
Impact
Successful exploitation could allow an attacker to execute arbitrary code with the privileges of the kubelet process on the Windows worker node. This could lead to full node compromise, container breakout, and lateral movement within the cluster. Given the critical role of worker nodes in running container workloads, this vulnerability poses a significant risk to cluster security [1].
Mitigation
Kubernetes has released patches for this vulnerability. The fix involves properly isolating environment variables for the logging command, as seen in the commits referenced [2][3][4]. Users should update their Kubernetes Windows worker nodes to the patched versions (typically Kubernetes 1.28.13+, 1.29.8+, 1.30.4+, and 1.31.0+). No specific workarounds are documented beyond applying the security update. The NVD has not yet fully enriched this record [1].
- NVD - CVE-2024-9042
- Merge pull request #129599 from aravindhp/automated-cherry-pick-of-#1… · kubernetes/kubernetes@75c83a6
- Merge pull request #129602 from aravindhp/automated-cherry-pick-of-#1… · kubernetes/kubernetes@45f4ccc
- Merge pull request #129603 from aravindhp/automated-cherry-pick-of-#1… · kubernetes/kubernetes@fb0187c
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
k8s.io/kubernetesGo | < 1.29.13 | 1.29.13 |
k8s.io/kubernetesGo | >= 1.30.0-alpha.0, < 1.30.9 | 1.30.9 |
k8s.io/kubernetesGo | >= 1.31.0-alpha.0, < 1.31.5 | 1.31.5 |
k8s.io/kubernetesGo | >= 1.32.0-alpha.0, < 1.32.1 | 1.32.1 |
Affected products
1- Range: >= 1.32.0-alpha.0, < 1.32.1
Patches
45fe148234f8aMerge pull request #129598 from aravindhp/automated-cherry-pick-of-#129595-upstream-release-1.32
9 files changed · +142 −37
pkg/features/kube_features.go+2 −1 modified@@ -446,7 +446,8 @@ const ( // owner: @aravindhp @LorbusChris // kep: http://kep.k8s.io/2271 // - // Enables querying logs of node services using the /logs endpoint + // Enables querying logs of node services using the /logs endpoint. Enabling this feature has security implications. + // The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. NodeLogQuery featuregate.Feature = "NodeLogQuery" // owner: @iholder101 @kannon92
pkg/generated/openapi/zz_generated.openapi.go+1 −1 modified@@ -64586,7 +64586,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen }, "enableSystemLogQuery": { SchemaProps: spec.SchemaProps{ - Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Default: false", + Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. Default: false", Type: []string{"boolean"}, Format: "", },
pkg/kubelet/apis/config/types.go+2 −0 modified@@ -413,6 +413,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler bool // EnableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // +featureGate=NodeLogQuery // +optional EnableSystemLogQuery bool
pkg/kubelet/kubelet_server_journal.go+2 −1 modified@@ -316,14 +316,15 @@ func (n *nodeLogQuery) splitNativeVsFileLoggers(ctx context.Context) ([]string, // copyServiceLogs invokes journalctl or Get-WinEvent with the provided args. Note that // services are explicitly passed here to account for the heuristics. func (n *nodeLogQuery) copyServiceLogs(ctx context.Context, w io.Writer, services []string, previousBoot int) { - cmdStr, args, err := getLoggingCmd(n, services) + cmdStr, args, cmdEnv, err := getLoggingCmd(n, services) if err != nil { fmt.Fprintf(w, "\nfailed to get logging cmd: %v\n", err) return } cmd := exec.CommandContext(ctx, cmdStr, args...) cmd.Stdout = w cmd.Stderr = w + cmd.Env = append(os.Environ(), cmdEnv...) if err := cmd.Run(); err != nil { if _, ok := err.(*exec.ExitError); ok {
pkg/kubelet/kubelet_server_journal_linux.go+8 −4 modified@@ -31,9 +31,13 @@ import ( ) // getLoggingCmd returns the journalctl cmd and arguments for the given nodeLogQuery and boot. Note that -// services are explicitly passed here to account for the heuristics -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// services are explicitly passed here to account for the heuristics. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + args = []string{ "--utc", "--no-pager", "--output=short-precise", @@ -60,7 +64,7 @@ func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) args = append(args, "--boot", fmt.Sprintf("%d", *n.Boot)) } - return "journalctl", args, nil + return "journalctl", args, nil, nil } // checkForNativeLogger checks journalctl output for a service
pkg/kubelet/kubelet_server_journal_others.go+2 −2 modified@@ -24,8 +24,8 @@ import ( ) // getLoggingCmd on unsupported operating systems returns the echo command and a warning message (as strings) -func getLoggingCmd(_ *nodeLogQuery, _ []string) (string, []string, error) { - return "", []string{}, errors.New("Operating System Not Supported") +func getLoggingCmd(_ *nodeLogQuery, _ []string) (cmd string, args []string, cmdEnv []string, err error) { + return "", args, cmdEnv, errors.New("Operating System Not Supported") } // checkForNativeLogger on unsupported operating systems returns false
pkg/kubelet/kubelet_server_journal_test.go+40 −9 modified@@ -35,31 +35,62 @@ import ( ) func Test_getLoggingCmd(t *testing.T) { + var emptyCmdEnv []string tests := []struct { - name string - args nodeLogQuery - wantLinux []string - wantWindows []string - wantOtherOS []string + name string + args nodeLogQuery + services []string + wantLinux []string + wantWindows []string + wantLinuxCmdEnv []string + wantWindowsCmdEnv []string }{ { - args: nodeLogQuery{}, - wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, - wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + name: "basic", + args: nodeLogQuery{}, + services: []string{}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, + wantLinuxCmdEnv: emptyCmdEnv, + wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + wantWindowsCmdEnv: emptyCmdEnv, + }, + { + name: "two providers", + args: nodeLogQuery{}, + services: []string{"p1", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider1=p2"}, + }, + { + name: "empty provider", + args: nodeLogQuery{}, + services: []string{"p1", "", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider2=p2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, got, err := getLoggingCmd(&tt.args, []string{}) + _, got, gotCmdEnv, err := getLoggingCmd(&tt.args, tt.services) switch os := runtime.GOOS; os { case "linux": if !reflect.DeepEqual(got, tt.wantLinux) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantLinux) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantLinuxCmdEnv) { + t.Errorf("gotCmdEnv %v, wantLinuxCmdEnv %v", gotCmdEnv, tt.wantLinuxCmdEnv) + } case "windows": if !reflect.DeepEqual(got, tt.wantWindows) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantWindows) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantWindowsCmdEnv) { + t.Errorf("gotCmdEnv %v, wantWindowsCmdEnv %v", gotCmdEnv, tt.wantWindowsCmdEnv) + } default: if err == nil { t.Errorf("getLoggingCmd() = %v, want err", got)
pkg/kubelet/kubelet_server_journal_windows.go+83 −19 modified@@ -27,43 +27,107 @@ import ( const powershellExe = "PowerShell.exe" -// getLoggingCmd returns the powershell cmd and arguments for the given nodeLogQuery and boot -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// getLoggingCmd returns the powershell cmd, arguments, and environment variables for the given nodeLogQuery and boot. +// All string inputs are environment variables to stop subcommands expressions from being executed. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + cmdEnv = getLoggingCmdEnv(n, services) + + var includeSinceTime, includeUntilTime, includeTailLines, includePattern bool + if n.SinceTime != nil { + includeSinceTime = true + } + if n.UntilTime != nil { + includeUntilTime = true + } + if n.TailLines != nil { + includeTailLines = true + } + if len(n.Pattern) > 0 { + includePattern = true + } + + var includeServices []bool + for _, service := range services { + includeServices = append(includeServices, len(service) > 0) + } + + args = getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern, includeServices) + + return powershellExe, args, cmdEnv, nil +} + +// getLoggingCmdArgs returns arguments that need to be passed to powershellExe +func getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern bool, services []bool) (args []string) { + args = []string{ "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", } - psCmd := "Get-WinEvent -FilterHashtable @{LogName='Application'" - if n.SinceTime != nil { - psCmd += fmt.Sprintf("; StartTime='%s'", n.SinceTime.Format(dateLayout)) + psCmd := `Get-WinEvent -FilterHashtable @{LogName='Application'` + + if includeSinceTime { + psCmd += fmt.Sprintf(`; StartTime="$Env:kubelet_sinceTime"`) } - if n.UntilTime != nil { - psCmd += fmt.Sprintf("; EndTime='%s'", n.UntilTime.Format(dateLayout)) + if includeUntilTime { + psCmd += fmt.Sprintf(`; EndTime="$Env:kubelet_untilTime"`) } + var providers []string - for _, service := range services { - if len(service) > 0 { - providers = append(providers, "'"+service+"'") + for i := range services { + if services[i] { + providers = append(providers, fmt.Sprintf("$Env:kubelet_provider%d", i)) } } + if len(providers) > 0 { psCmd += fmt.Sprintf("; ProviderName=%s", strings.Join(providers, ",")) } - psCmd += "}" - if n.TailLines != nil { - psCmd += fmt.Sprintf(" -MaxEvents %d", *n.TailLines) + + psCmd += `}` + if includeTailLines { + psCmd += fmt.Sprint(` -MaxEvents $Env:kubelet_tailLines`) } - psCmd += " | Sort-Object TimeCreated" - if len(n.Pattern) > 0 { - psCmd += fmt.Sprintf(" | Where-Object -Property Message -Match '%s'", n.Pattern) + psCmd += ` | Sort-Object TimeCreated` + + if includePattern { + psCmd += fmt.Sprintf(` | Where-Object -Property Message -Match "$Env:kubelet_pattern"`) } - psCmd += " | Format-Table -AutoSize -Wrap" + psCmd += ` | Format-Table -AutoSize -Wrap` args = append(args, psCmd) - return powershellExe, args, nil + return args +} + +// getLoggingCmdEnv returns the environment variables that will be present when powershellExe is executed +func getLoggingCmdEnv(n *nodeLogQuery, services []string) (cmdEnv []string) { + if n.SinceTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_sinceTime=%s", n.SinceTime.Format(dateLayout))) + } + if n.UntilTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_untilTime=%s", n.UntilTime.Format(dateLayout))) + } + + for i, service := range services { + if len(service) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_provider%d=%s", i, service)) + } + } + + if n.TailLines != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_tailLines=%d", *n.TailLines)) + } + + if len(n.Pattern) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_pattern=%s", n.Pattern)) + } + + return cmdEnv } // checkForNativeLogger always returns true for Windows
staging/src/k8s.io/kubelet/config/v1beta1/types.go+2 −0 modified@@ -726,6 +726,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler *bool `json:"enableSystemLogHandler,omitempty"` // enableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // Default: false // +featureGate=NodeLogQuery // +optional
75c83a6871dcMerge pull request #129599 from aravindhp/automated-cherry-pick-of-#129595-upstream-release-1.31
9 files changed · +142 −37
pkg/features/kube_features.go+2 −1 modified@@ -524,7 +524,8 @@ const ( // alpha: v1.27 // beta: v1.30 // - // Enables querying logs of node services using the /logs endpoint + // Enables querying logs of node services using the /logs endpoint. Enabling this feature has security implications. + // The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. NodeLogQuery featuregate.Feature = "NodeLogQuery" // owner: @xing-yang @sonasingh46
pkg/generated/openapi/zz_generated.openapi.go+1 −1 modified@@ -62689,7 +62689,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen }, "enableSystemLogQuery": { SchemaProps: spec.SchemaProps{ - Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Default: false", + Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. Default: false", Type: []string{"boolean"}, Format: "", },
pkg/kubelet/apis/config/types.go+2 −0 modified@@ -408,6 +408,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler bool // EnableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // +featureGate=NodeLogQuery // +optional EnableSystemLogQuery bool
pkg/kubelet/kubelet_server_journal.go+2 −1 modified@@ -316,14 +316,15 @@ func (n *nodeLogQuery) splitNativeVsFileLoggers(ctx context.Context) ([]string, // copyServiceLogs invokes journalctl or Get-WinEvent with the provided args. Note that // services are explicitly passed here to account for the heuristics. func (n *nodeLogQuery) copyServiceLogs(ctx context.Context, w io.Writer, services []string, previousBoot int) { - cmdStr, args, err := getLoggingCmd(n, services) + cmdStr, args, cmdEnv, err := getLoggingCmd(n, services) if err != nil { fmt.Fprintf(w, "\nfailed to get logging cmd: %v\n", err) return } cmd := exec.CommandContext(ctx, cmdStr, args...) cmd.Stdout = w cmd.Stderr = w + cmd.Env = append(os.Environ(), cmdEnv...) if err := cmd.Run(); err != nil { if _, ok := err.(*exec.ExitError); ok {
pkg/kubelet/kubelet_server_journal_linux.go+8 −4 modified@@ -26,9 +26,13 @@ import ( ) // getLoggingCmd returns the journalctl cmd and arguments for the given nodeLogQuery and boot. Note that -// services are explicitly passed here to account for the heuristics -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// services are explicitly passed here to account for the heuristics. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + args = []string{ "--utc", "--no-pager", "--output=short-precise", @@ -55,7 +59,7 @@ func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) args = append(args, "--boot", fmt.Sprintf("%d", *n.Boot)) } - return "journalctl", args, nil + return "journalctl", args, nil, nil } // checkForNativeLogger checks journalctl output for a service
pkg/kubelet/kubelet_server_journal_others.go+2 −2 modified@@ -24,8 +24,8 @@ import ( ) // getLoggingCmd on unsupported operating systems returns the echo command and a warning message (as strings) -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - return "", []string{}, errors.New("Operating System Not Supported") +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + return "", args, cmdEnv, errors.New("Operating System Not Supported") } // checkForNativeLogger on unsupported operating systems returns false
pkg/kubelet/kubelet_server_journal_test.go+40 −9 modified@@ -30,31 +30,62 @@ import ( ) func Test_getLoggingCmd(t *testing.T) { + var emptyCmdEnv []string tests := []struct { - name string - args nodeLogQuery - wantLinux []string - wantWindows []string - wantOtherOS []string + name string + args nodeLogQuery + services []string + wantLinux []string + wantWindows []string + wantLinuxCmdEnv []string + wantWindowsCmdEnv []string }{ { - args: nodeLogQuery{}, - wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, - wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + name: "basic", + args: nodeLogQuery{}, + services: []string{}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, + wantLinuxCmdEnv: emptyCmdEnv, + wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + wantWindowsCmdEnv: emptyCmdEnv, + }, + { + name: "two providers", + args: nodeLogQuery{}, + services: []string{"p1", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider1=p2"}, + }, + { + name: "empty provider", + args: nodeLogQuery{}, + services: []string{"p1", "", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider2=p2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, got, err := getLoggingCmd(&tt.args, []string{}) + _, got, gotCmdEnv, err := getLoggingCmd(&tt.args, tt.services) switch os := runtime.GOOS; os { case "linux": if !reflect.DeepEqual(got, tt.wantLinux) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantLinux) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantLinuxCmdEnv) { + t.Errorf("gotCmdEnv %v, wantLinuxCmdEnv %v", gotCmdEnv, tt.wantLinuxCmdEnv) + } case "windows": if !reflect.DeepEqual(got, tt.wantWindows) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantWindows) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantWindowsCmdEnv) { + t.Errorf("gotCmdEnv %v, wantWindowsCmdEnv %v", gotCmdEnv, tt.wantWindowsCmdEnv) + } default: if err == nil { t.Errorf("getLoggingCmd() = %v, want err", got)
pkg/kubelet/kubelet_server_journal_windows.go+83 −19 modified@@ -27,43 +27,107 @@ import ( const powershellExe = "PowerShell.exe" -// getLoggingCmd returns the powershell cmd and arguments for the given nodeLogQuery and boot -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// getLoggingCmd returns the powershell cmd, arguments, and environment variables for the given nodeLogQuery and boot. +// All string inputs are environment variables to stop subcommands expressions from being executed. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + cmdEnv = getLoggingCmdEnv(n, services) + + var includeSinceTime, includeUntilTime, includeTailLines, includePattern bool + if n.SinceTime != nil { + includeSinceTime = true + } + if n.UntilTime != nil { + includeUntilTime = true + } + if n.TailLines != nil { + includeTailLines = true + } + if len(n.Pattern) > 0 { + includePattern = true + } + + var includeServices []bool + for _, service := range services { + includeServices = append(includeServices, len(service) > 0) + } + + args = getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern, includeServices) + + return powershellExe, args, cmdEnv, nil +} + +// getLoggingCmdArgs returns arguments that need to be passed to powershellExe +func getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern bool, services []bool) (args []string) { + args = []string{ "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", } - psCmd := "Get-WinEvent -FilterHashtable @{LogName='Application'" - if n.SinceTime != nil { - psCmd += fmt.Sprintf("; StartTime='%s'", n.SinceTime.Format(dateLayout)) + psCmd := `Get-WinEvent -FilterHashtable @{LogName='Application'` + + if includeSinceTime { + psCmd += fmt.Sprintf(`; StartTime="$Env:kubelet_sinceTime"`) } - if n.UntilTime != nil { - psCmd += fmt.Sprintf("; EndTime='%s'", n.UntilTime.Format(dateLayout)) + if includeUntilTime { + psCmd += fmt.Sprintf(`; EndTime="$Env:kubelet_untilTime"`) } + var providers []string - for _, service := range services { - if len(service) > 0 { - providers = append(providers, "'"+service+"'") + for i := range services { + if services[i] { + providers = append(providers, fmt.Sprintf("$Env:kubelet_provider%d", i)) } } + if len(providers) > 0 { psCmd += fmt.Sprintf("; ProviderName=%s", strings.Join(providers, ",")) } - psCmd += "}" - if n.TailLines != nil { - psCmd += fmt.Sprintf(" -MaxEvents %d", *n.TailLines) + + psCmd += `}` + if includeTailLines { + psCmd += fmt.Sprint(` -MaxEvents $Env:kubelet_tailLines`) } - psCmd += " | Sort-Object TimeCreated" - if len(n.Pattern) > 0 { - psCmd += fmt.Sprintf(" | Where-Object -Property Message -Match '%s'", n.Pattern) + psCmd += ` | Sort-Object TimeCreated` + + if includePattern { + psCmd += fmt.Sprintf(` | Where-Object -Property Message -Match "$Env:kubelet_pattern"`) } - psCmd += " | Format-Table -AutoSize -Wrap" + psCmd += ` | Format-Table -AutoSize -Wrap` args = append(args, psCmd) - return powershellExe, args, nil + return args +} + +// getLoggingCmdEnv returns the environment variables that will be present when powershellExe is executed +func getLoggingCmdEnv(n *nodeLogQuery, services []string) (cmdEnv []string) { + if n.SinceTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_sinceTime=%s", n.SinceTime.Format(dateLayout))) + } + if n.UntilTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_untilTime=%s", n.UntilTime.Format(dateLayout))) + } + + for i, service := range services { + if len(service) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_provider%d=%s", i, service)) + } + } + + if n.TailLines != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_tailLines=%d", *n.TailLines)) + } + + if len(n.Pattern) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_pattern=%s", n.Pattern)) + } + + return cmdEnv } // checkForNativeLogger always returns true for Windows
staging/src/k8s.io/kubelet/config/v1beta1/types.go+2 −0 modified@@ -720,6 +720,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler *bool `json:"enableSystemLogHandler,omitempty"` // enableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // Default: false // +featureGate=NodeLogQuery // +optional
fb0187c2bf70Merge pull request #129603 from aravindhp/automated-cherry-pick-of-#129599-upstream-release-1.29
9 files changed · +142 −37
pkg/features/kube_features.go+2 −1 modified@@ -586,7 +586,8 @@ const ( // kep: http://kep.k8s.io/2271 // alpha: v1.27 // - // Enables querying logs of node services using the /logs endpoint + // Enables querying logs of node services using the /logs endpoint. Enabling this feature has security implications. + // The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. NodeLogQuery featuregate.Feature = "NodeLogQuery" // owner: @xing-yang @sonasingh46
pkg/generated/openapi/zz_generated.openapi.go+1 −1 modified@@ -57551,7 +57551,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen }, "enableSystemLogQuery": { SchemaProps: spec.SchemaProps{ - Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Default: false", + Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. Default: false", Type: []string{"boolean"}, Format: "", },
pkg/kubelet/apis/config/types.go+2 −0 modified@@ -398,6 +398,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler bool // EnableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // +featureGate=NodeLogQuery // +optional EnableSystemLogQuery bool
pkg/kubelet/kubelet_server_journal.go+2 −1 modified@@ -316,14 +316,15 @@ func (n *nodeLogQuery) splitNativeVsFileLoggers(ctx context.Context) ([]string, // copyServiceLogs invokes journalctl or Get-WinEvent with the provided args. Note that // services are explicitly passed here to account for the heuristics. func (n *nodeLogQuery) copyServiceLogs(ctx context.Context, w io.Writer, services []string, previousBoot int) { - cmdStr, args, err := getLoggingCmd(n, services) + cmdStr, args, cmdEnv, err := getLoggingCmd(n, services) if err != nil { fmt.Fprintf(w, "\nfailed to get logging cmd: %v\n", err) return } cmd := exec.CommandContext(ctx, cmdStr, args...) cmd.Stdout = w cmd.Stderr = w + cmd.Env = append(os.Environ(), cmdEnv...) if err := cmd.Run(); err != nil { if _, ok := err.(*exec.ExitError); ok {
pkg/kubelet/kubelet_server_journal_linux.go+8 −4 modified@@ -26,9 +26,13 @@ import ( ) // getLoggingCmd returns the journalctl cmd and arguments for the given nodeLogQuery and boot. Note that -// services are explicitly passed here to account for the heuristics -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// services are explicitly passed here to account for the heuristics. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + args = []string{ "--utc", "--no-pager", "--output=short-precise", @@ -55,7 +59,7 @@ func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) args = append(args, "--boot", fmt.Sprintf("%d", *n.Boot)) } - return "journalctl", args, nil + return "journalctl", args, nil, nil } // checkForNativeLogger checks journalctl output for a service
pkg/kubelet/kubelet_server_journal_others.go+2 −2 modified@@ -24,8 +24,8 @@ import ( ) // getLoggingCmd on unsupported operating systems returns the echo command and a warning message (as strings) -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - return "", []string{}, errors.New("Operating System Not Supported") +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + return "", args, cmdEnv, errors.New("Operating System Not Supported") } // checkForNativeLogger on unsupported operating systems returns false
pkg/kubelet/kubelet_server_journal_test.go+40 −9 modified@@ -30,31 +30,62 @@ import ( ) func Test_getLoggingCmd(t *testing.T) { + var emptyCmdEnv []string tests := []struct { - name string - args nodeLogQuery - wantLinux []string - wantWindows []string - wantOtherOS []string + name string + args nodeLogQuery + services []string + wantLinux []string + wantWindows []string + wantLinuxCmdEnv []string + wantWindowsCmdEnv []string }{ { - args: nodeLogQuery{}, - wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, - wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + name: "basic", + args: nodeLogQuery{}, + services: []string{}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, + wantLinuxCmdEnv: emptyCmdEnv, + wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + wantWindowsCmdEnv: emptyCmdEnv, + }, + { + name: "two providers", + args: nodeLogQuery{}, + services: []string{"p1", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider1=p2"}, + }, + { + name: "empty provider", + args: nodeLogQuery{}, + services: []string{"p1", "", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider2=p2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, got, err := getLoggingCmd(&tt.args, []string{}) + _, got, gotCmdEnv, err := getLoggingCmd(&tt.args, tt.services) switch os := runtime.GOOS; os { case "linux": if !reflect.DeepEqual(got, tt.wantLinux) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantLinux) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantLinuxCmdEnv) { + t.Errorf("gotCmdEnv %v, wantLinuxCmdEnv %v", gotCmdEnv, tt.wantLinuxCmdEnv) + } case "windows": if !reflect.DeepEqual(got, tt.wantWindows) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantWindows) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantWindowsCmdEnv) { + t.Errorf("gotCmdEnv %v, wantWindowsCmdEnv %v", gotCmdEnv, tt.wantWindowsCmdEnv) + } default: if err == nil { t.Errorf("getLoggingCmd() = %v, want err", got)
pkg/kubelet/kubelet_server_journal_windows.go+83 −19 modified@@ -27,43 +27,107 @@ import ( const powershellExe = "PowerShell.exe" -// getLoggingCmd returns the powershell cmd and arguments for the given nodeLogQuery and boot -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// getLoggingCmd returns the powershell cmd, arguments, and environment variables for the given nodeLogQuery and boot. +// All string inputs are environment variables to stop subcommands expressions from being executed. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + cmdEnv = getLoggingCmdEnv(n, services) + + var includeSinceTime, includeUntilTime, includeTailLines, includePattern bool + if n.SinceTime != nil { + includeSinceTime = true + } + if n.UntilTime != nil { + includeUntilTime = true + } + if n.TailLines != nil { + includeTailLines = true + } + if len(n.Pattern) > 0 { + includePattern = true + } + + var includeServices []bool + for _, service := range services { + includeServices = append(includeServices, len(service) > 0) + } + + args = getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern, includeServices) + + return powershellExe, args, cmdEnv, nil +} + +// getLoggingCmdArgs returns arguments that need to be passed to powershellExe +func getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern bool, services []bool) (args []string) { + args = []string{ "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", } - psCmd := "Get-WinEvent -FilterHashtable @{LogName='Application'" - if n.SinceTime != nil { - psCmd += fmt.Sprintf("; StartTime='%s'", n.SinceTime.Format(dateLayout)) + psCmd := `Get-WinEvent -FilterHashtable @{LogName='Application'` + + if includeSinceTime { + psCmd += fmt.Sprintf(`; StartTime="$Env:kubelet_sinceTime"`) } - if n.UntilTime != nil { - psCmd += fmt.Sprintf("; EndTime='%s'", n.UntilTime.Format(dateLayout)) + if includeUntilTime { + psCmd += fmt.Sprintf(`; EndTime="$Env:kubelet_untilTime"`) } + var providers []string - for _, service := range services { - if len(service) > 0 { - providers = append(providers, "'"+service+"'") + for i := range services { + if services[i] { + providers = append(providers, fmt.Sprintf("$Env:kubelet_provider%d", i)) } } + if len(providers) > 0 { psCmd += fmt.Sprintf("; ProviderName=%s", strings.Join(providers, ",")) } - psCmd += "}" - if n.TailLines != nil { - psCmd += fmt.Sprintf(" -MaxEvents %d", *n.TailLines) + + psCmd += `}` + if includeTailLines { + psCmd += fmt.Sprint(` -MaxEvents $Env:kubelet_tailLines`) } - psCmd += " | Sort-Object TimeCreated" - if len(n.Pattern) > 0 { - psCmd += fmt.Sprintf(" | Where-Object -Property Message -Match '%s'", n.Pattern) + psCmd += ` | Sort-Object TimeCreated` + + if includePattern { + psCmd += fmt.Sprintf(` | Where-Object -Property Message -Match "$Env:kubelet_pattern"`) } - psCmd += " | Format-Table -AutoSize -Wrap" + psCmd += ` | Format-Table -AutoSize -Wrap` args = append(args, psCmd) - return powershellExe, args, nil + return args +} + +// getLoggingCmdEnv returns the environment variables that will be present when powershellExe is executed +func getLoggingCmdEnv(n *nodeLogQuery, services []string) (cmdEnv []string) { + if n.SinceTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_sinceTime=%s", n.SinceTime.Format(dateLayout))) + } + if n.UntilTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_untilTime=%s", n.UntilTime.Format(dateLayout))) + } + + for i, service := range services { + if len(service) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_provider%d=%s", i, service)) + } + } + + if n.TailLines != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_tailLines=%d", *n.TailLines)) + } + + if len(n.Pattern) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_pattern=%s", n.Pattern)) + } + + return cmdEnv } // checkForNativeLogger always returns true for Windows
staging/src/k8s.io/kubelet/config/v1beta1/types.go+2 −0 modified@@ -699,6 +699,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler *bool `json:"enableSystemLogHandler,omitempty"` // enableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // Default: false // +featureGate=NodeLogQuery // +optional
45f4ccc2153bMerge pull request #129602 from aravindhp/automated-cherry-pick-of-#129599-upstream-release-1.30
9 files changed · +142 −37
pkg/features/kube_features.go+2 −1 modified@@ -541,7 +541,8 @@ const ( // alpha: v1.27 // beta: v1.30 // - // Enables querying logs of node services using the /logs endpoint + // Enables querying logs of node services using the /logs endpoint. Enabling this feature has security implications. + // The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. NodeLogQuery featuregate.Feature = "NodeLogQuery" // owner: @xing-yang @sonasingh46
pkg/generated/openapi/zz_generated.openapi.go+1 −1 modified@@ -61936,7 +61936,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen }, "enableSystemLogQuery": { SchemaProps: spec.SchemaProps{ - Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Default: false", + Description: "enableSystemLogQuery enables the node log query feature on the /logs endpoint. EnableSystemLogHandler has to be enabled in addition for this feature to work. Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging purposes and disabling otherwise. Default: false", Type: []string{"boolean"}, Format: "", },
pkg/kubelet/apis/config/types.go+2 −0 modified@@ -408,6 +408,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler bool // EnableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // +featureGate=NodeLogQuery // +optional EnableSystemLogQuery bool
pkg/kubelet/kubelet_server_journal.go+2 −1 modified@@ -316,14 +316,15 @@ func (n *nodeLogQuery) splitNativeVsFileLoggers(ctx context.Context) ([]string, // copyServiceLogs invokes journalctl or Get-WinEvent with the provided args. Note that // services are explicitly passed here to account for the heuristics. func (n *nodeLogQuery) copyServiceLogs(ctx context.Context, w io.Writer, services []string, previousBoot int) { - cmdStr, args, err := getLoggingCmd(n, services) + cmdStr, args, cmdEnv, err := getLoggingCmd(n, services) if err != nil { fmt.Fprintf(w, "\nfailed to get logging cmd: %v\n", err) return } cmd := exec.CommandContext(ctx, cmdStr, args...) cmd.Stdout = w cmd.Stderr = w + cmd.Env = append(os.Environ(), cmdEnv...) if err := cmd.Run(); err != nil { if _, ok := err.(*exec.ExitError); ok {
pkg/kubelet/kubelet_server_journal_linux.go+8 −4 modified@@ -26,9 +26,13 @@ import ( ) // getLoggingCmd returns the journalctl cmd and arguments for the given nodeLogQuery and boot. Note that -// services are explicitly passed here to account for the heuristics -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// services are explicitly passed here to account for the heuristics. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + args = []string{ "--utc", "--no-pager", "--output=short-precise", @@ -55,7 +59,7 @@ func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) args = append(args, "--boot", fmt.Sprintf("%d", *n.Boot)) } - return "journalctl", args, nil + return "journalctl", args, nil, nil } // checkForNativeLogger checks journalctl output for a service
pkg/kubelet/kubelet_server_journal_others.go+2 −2 modified@@ -24,8 +24,8 @@ import ( ) // getLoggingCmd on unsupported operating systems returns the echo command and a warning message (as strings) -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - return "", []string{}, errors.New("Operating System Not Supported") +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + return "", args, cmdEnv, errors.New("Operating System Not Supported") } // checkForNativeLogger on unsupported operating systems returns false
pkg/kubelet/kubelet_server_journal_test.go+40 −9 modified@@ -30,31 +30,62 @@ import ( ) func Test_getLoggingCmd(t *testing.T) { + var emptyCmdEnv []string tests := []struct { - name string - args nodeLogQuery - wantLinux []string - wantWindows []string - wantOtherOS []string + name string + args nodeLogQuery + services []string + wantLinux []string + wantWindows []string + wantLinuxCmdEnv []string + wantWindowsCmdEnv []string }{ { - args: nodeLogQuery{}, - wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, - wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + name: "basic", + args: nodeLogQuery{}, + services: []string{}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise"}, + wantLinuxCmdEnv: emptyCmdEnv, + wantWindows: []string{"-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "Get-WinEvent -FilterHashtable @{LogName='Application'} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap"}, + wantWindowsCmdEnv: emptyCmdEnv, + }, + { + name: "two providers", + args: nodeLogQuery{}, + services: []string{"p1", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider1=p2"}, + }, + { + name: "empty provider", + args: nodeLogQuery{}, + services: []string{"p1", "", "p2"}, + wantLinux: []string{"--utc", "--no-pager", "--output=short-precise", "--unit=p1", "--unit=p2"}, + wantLinuxCmdEnv: emptyCmdEnv, + 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"}, + wantWindowsCmdEnv: []string{"kubelet_provider0=p1", "kubelet_provider2=p2"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, got, err := getLoggingCmd(&tt.args, []string{}) + _, got, gotCmdEnv, err := getLoggingCmd(&tt.args, tt.services) switch os := runtime.GOOS; os { case "linux": if !reflect.DeepEqual(got, tt.wantLinux) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantLinux) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantLinuxCmdEnv) { + t.Errorf("gotCmdEnv %v, wantLinuxCmdEnv %v", gotCmdEnv, tt.wantLinuxCmdEnv) + } case "windows": if !reflect.DeepEqual(got, tt.wantWindows) { t.Errorf("getLoggingCmd() = %v, want %v", got, tt.wantWindows) } + if !reflect.DeepEqual(gotCmdEnv, tt.wantWindowsCmdEnv) { + t.Errorf("gotCmdEnv %v, wantWindowsCmdEnv %v", gotCmdEnv, tt.wantWindowsCmdEnv) + } default: if err == nil { t.Errorf("getLoggingCmd() = %v, want err", got)
pkg/kubelet/kubelet_server_journal_windows.go+83 −19 modified@@ -27,43 +27,107 @@ import ( const powershellExe = "PowerShell.exe" -// getLoggingCmd returns the powershell cmd and arguments for the given nodeLogQuery and boot -func getLoggingCmd(n *nodeLogQuery, services []string) (string, []string, error) { - args := []string{ +// getLoggingCmd returns the powershell cmd, arguments, and environment variables for the given nodeLogQuery and boot. +// All string inputs are environment variables to stop subcommands expressions from being executed. +// The return values are: +// - cmd: the command to be executed +// - args: arguments to the command +// - cmdEnv: environment variables when the command will be executed +func getLoggingCmd(n *nodeLogQuery, services []string) (cmd string, args []string, cmdEnv []string, err error) { + cmdEnv = getLoggingCmdEnv(n, services) + + var includeSinceTime, includeUntilTime, includeTailLines, includePattern bool + if n.SinceTime != nil { + includeSinceTime = true + } + if n.UntilTime != nil { + includeUntilTime = true + } + if n.TailLines != nil { + includeTailLines = true + } + if len(n.Pattern) > 0 { + includePattern = true + } + + var includeServices []bool + for _, service := range services { + includeServices = append(includeServices, len(service) > 0) + } + + args = getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern, includeServices) + + return powershellExe, args, cmdEnv, nil +} + +// getLoggingCmdArgs returns arguments that need to be passed to powershellExe +func getLoggingCmdArgs(includeSinceTime, includeUntilTime, includeTailLines, includePattern bool, services []bool) (args []string) { + args = []string{ "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", } - psCmd := "Get-WinEvent -FilterHashtable @{LogName='Application'" - if n.SinceTime != nil { - psCmd += fmt.Sprintf("; StartTime='%s'", n.SinceTime.Format(dateLayout)) + psCmd := `Get-WinEvent -FilterHashtable @{LogName='Application'` + + if includeSinceTime { + psCmd += fmt.Sprintf(`; StartTime="$Env:kubelet_sinceTime"`) } - if n.UntilTime != nil { - psCmd += fmt.Sprintf("; EndTime='%s'", n.UntilTime.Format(dateLayout)) + if includeUntilTime { + psCmd += fmt.Sprintf(`; EndTime="$Env:kubelet_untilTime"`) } + var providers []string - for _, service := range services { - if len(service) > 0 { - providers = append(providers, "'"+service+"'") + for i := range services { + if services[i] { + providers = append(providers, fmt.Sprintf("$Env:kubelet_provider%d", i)) } } + if len(providers) > 0 { psCmd += fmt.Sprintf("; ProviderName=%s", strings.Join(providers, ",")) } - psCmd += "}" - if n.TailLines != nil { - psCmd += fmt.Sprintf(" -MaxEvents %d", *n.TailLines) + + psCmd += `}` + if includeTailLines { + psCmd += fmt.Sprint(` -MaxEvents $Env:kubelet_tailLines`) } - psCmd += " | Sort-Object TimeCreated" - if len(n.Pattern) > 0 { - psCmd += fmt.Sprintf(" | Where-Object -Property Message -Match '%s'", n.Pattern) + psCmd += ` | Sort-Object TimeCreated` + + if includePattern { + psCmd += fmt.Sprintf(` | Where-Object -Property Message -Match "$Env:kubelet_pattern"`) } - psCmd += " | Format-Table -AutoSize -Wrap" + psCmd += ` | Format-Table -AutoSize -Wrap` args = append(args, psCmd) - return powershellExe, args, nil + return args +} + +// getLoggingCmdEnv returns the environment variables that will be present when powershellExe is executed +func getLoggingCmdEnv(n *nodeLogQuery, services []string) (cmdEnv []string) { + if n.SinceTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_sinceTime=%s", n.SinceTime.Format(dateLayout))) + } + if n.UntilTime != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_untilTime=%s", n.UntilTime.Format(dateLayout))) + } + + for i, service := range services { + if len(service) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_provider%d=%s", i, service)) + } + } + + if n.TailLines != nil { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_tailLines=%d", *n.TailLines)) + } + + if len(n.Pattern) > 0 { + cmdEnv = append(cmdEnv, fmt.Sprintf("kubelet_pattern=%s", n.Pattern)) + } + + return cmdEnv } // checkForNativeLogger always returns true for Windows
staging/src/k8s.io/kubelet/config/v1beta1/types.go+2 −0 modified@@ -720,6 +720,8 @@ type KubeletConfiguration struct { EnableSystemLogHandler *bool `json:"enableSystemLogHandler,omitempty"` // enableSystemLogQuery enables the node log query feature on the /logs endpoint. // EnableSystemLogHandler has to be enabled in addition for this feature to work. + // Enabling this feature has security implications. The recommendation is to enable it on a need basis for debugging + // purposes and disabling otherwise. // Default: false // +featureGate=NodeLogQuery // +optional
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-vv39-3w5q-974qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-9042ghsaADVISORY
- www.openwall.com/lists/oss-security/2025/01/16/1nvdWEB
- github.com/kubernetes/kubernetes/commit/45f4ccc2153bbb782253704cbe24c05e22b5d60cghsaWEB
- github.com/kubernetes/kubernetes/commit/5fe148234f8ab1184f26069c4f7bef6c37efe347ghsaWEB
- github.com/kubernetes/kubernetes/commit/75c83a6871dc030675288c6d63c275a43c2f0d55ghsaWEB
- github.com/kubernetes/kubernetes/commit/fb0187c2bf7061258bb89891edb1237261eb7abcghsaWEB
- github.com/kubernetes/kubernetes/issues/129654nvdWEB
- groups.google.com/g/kubernetes-security-announce/c/9C3vn6aCSVgnvdWEB
News mentions
0No linked articles in our index yet.