podman-desktop
podman-desktop copied to clipboard
Kubectl update fails
Bug description
I'm at kubectl 2.24.5, and Settings > CLI Tools says there is an update available to 1.29.1. When I click on the button to upgrade it prompts for my password and then says Unable to update kubectl to version 1.29.1.
, which in the Tasks shows Error: Command execution failed with exit code 1
First, this shouldn't fail. :) Second, it would be helpful if the error message showed something more than a '1' failure (any output from the command?) or what it was trying to do when it failed so that I can try it externally.
Operating system
macOS 14.3
Installation Method
None
Version
1.7.0
Steps to reproduce
No response
Relevant log output
In the logs I only have the following:
1:46:46 p.m. [main] [kubectl-cli] Made /usr/local/bin/kubectl executable
1:46:51 p.m. [main] [kubectl-cli] Failed to install 'kubectl' binary: Command execution failed with exit code 1: Command execution failed with exit code 1
### Additional context
_No response_
This happens on fedora 39 as well.
It happens also on Windows 11.
Error message:
Error: Failed to execute command: Command failed: copy "C:\Users\<my-windows-user-name>\.local\bin\kubectl.exeC:\Users\<my-windows-user-name>\AppData\Local\Microsoft\WindowsApps\kubectl.exe","C:\Users\<my-windows-user-name>\AppData\Local\Microsoft\WindowsApps\kubectl.exe" The filename, directory name, or volume label syntax is incorrect.
I couldn't find why the first argument is ${existingBin}${destination}
form yet.
But this is where ,
is introduced:
https://github.com/containers/podman-desktop/blob/0547e2c7545264dfb9561ec18e17177d95ef77d0/packages/main/src/plugin/util/exec.ts#L94
I think it was supposed to be ${(args || []).join(' ')}
, not ${args || [].join(' ')}
(operator precedence).
But just correcting parenthesis isn't enough. I usually bet there's some fractal of multiple layers of convoluted command injection situation deep underneath it when I see these kinds of somewhat uncanny paths and quotations.
They commonly involve string manipulations (concat, format string, etc, ...), which is generally not suitable for dealing with serializing structural data, to construct a command line, without knowing that they are actually building a shell script, which is a structural data (Syntax tree), for implicitly assumed shells. They are brittle, so an excess amount of quoting and escaping is added, and it is more brittle. Without knowing which exact shell will execute it and how they are manipulated during the process after it, it is trivial to introduce complexity.
This is the entry point of it:
https://github.com/containers/podman-desktop/blob/main/extensions/kubectl-cli/src/cli-run.ts#L50-L62
It is somewhat better for Mac, but for Linux, string concat without any quoting or escaping to build shell script, that will be run as sudo with /bin/sh
.
And it is much worse for windows.
The args are joined with ${args || [].join(' ')}
, then https://github.com/jorangreef/sudo-prompt will handle the rest.
And you wouldn't sleep well like before if you know how sudo-prompt
handles privilege escalation:
It uses PowerShell to invoke elevated cmd.exe, to run a generated execute.bat
which will run and capture the outputs of a generated command.bat
that invokes a requested command.
Usually, I assume there is a shell when an exec
like function accepts just a single string argument. And I always try to avoid a shell in the middle at all costs.
It is much more simpler and easier when you can just use execv(3)
style function that accepts the argument as an array of strings. You can completely forget about the quoting and escaping. They are shell things, not OS things (except for Windows)
You just pass an array of args without touching them. It'll preserve the argument list structure. If you have to serialize them to cross the process boundary, you usually might be able to use proper serialization formats; json is common. Xml can be used for PowerShell. Actually PowerShell's rich named argument system and object stream use Xml under the hood.
example of exec
with a single string argument: https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback
example of execv(3)
like function: https://nodejs.org/api/child_process.html#child_processexecfilefile-args-options-callback
It wouldn't be that easy for Windows since it doesn't have execv(3)
like system call. (but I think it is doable)
Windows's CreateProcess call has LPSTR commandline
argument (and its behavior is super weird. see: https://learn.microsoft.com/ko-kr/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa), and application only can read provided whole commandline by default with GetCommandLine function (https://learn.microsoft.com/ko-kr/windows/win32/api/processenv/nf-processenv-getcommandlinea)
Modern shells and libraries for Windows that accept a list of strings for arguments will apply appropriate escaping and quoting.
And it must do an impossible task: Anticipate how the executable will parse the escaped/quoted commandline without executing it.
https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170
Fortunately, it'll be usually handled by MSVCRT's argument parsing library before entering main
when the program is built with modern MSVC, so it works most of the time (except when it doesn't)
(pwsh.exe
has a list of programs that don't like quoting: https://github.com/PowerShell/PowerShell/blob/0919240822e032af91dc2fe4ce481e82002b92c0/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L197-L217)
But since sudo-prompt assumes PowerShell's existance, we might be able to use its xml based args.
I've tried and reproduce the error
weirdly the command is trying to copy on itself a file... Getting the error Copy-Item: Cannot overwrite the item C:\Users\axels\AppData\Local\Microsoft\WindowsApps\kubectl.exe with itself.
The issue seems to be coming from path provided to installBinaryToSystem
, which seems to be the one where the executable is installed system wide instead of where the downloaded