VYPR
Medium severity5.9GHSA Advisory· Published Mar 13, 2025· Updated Apr 15, 2026

CVE-2024-9042

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].

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.

PackageAffected versionsPatched versions
k8s.io/kubernetesGo
< 1.29.131.29.13
k8s.io/kubernetesGo
>= 1.30.0-alpha.0, < 1.30.91.30.9
k8s.io/kubernetesGo
>= 1.31.0-alpha.0, < 1.31.51.31.5
k8s.io/kubernetesGo
>= 1.32.0-alpha.0, < 1.32.11.32.1

Affected products

1

Patches

4
5fe148234f8a

Merge pull request #129598 from aravindhp/automated-cherry-pick-of-#129595-upstream-release-1.32

https://github.com/kubernetes/kubernetesKubernetes Prow RobotJan 14, 2025via ghsa
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
    
75c83a6871dc

Merge pull request #129599 from aravindhp/automated-cherry-pick-of-#129595-upstream-release-1.31

https://github.com/kubernetes/kubernetesKubernetes Prow RobotJan 14, 2025via ghsa
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
    
fb0187c2bf70

Merge pull request #129603 from aravindhp/automated-cherry-pick-of-#129599-upstream-release-1.29

https://github.com/kubernetes/kubernetesKubernetes Prow RobotJan 14, 2025via ghsa
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
    
45f4ccc2153b

Merge pull request #129602 from aravindhp/automated-cherry-pick-of-#129599-upstream-release-1.30

https://github.com/kubernetes/kubernetesKubernetes Prow RobotJan 14, 2025via ghsa
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

News mentions

0

No linked articles in our index yet.