Files
ubicloud/cli-commands/vm/ssh.rb
Jeremy Evans 912b2fbc30 Refactor how exec command args are passed from server to ubi
Previously, there was a set of headers used:

* ubi-command-execute: For the command to execute
* ubi-command-arg: For the argument to the command
* ubi-command-argv-tail: For the argv index to include the rest of
  argv in command
* ubi-command-argv-initial: For the argv index to include before the
  argument to the command

This was too limiting, as it doesn't allow specifying both options
and arguments to exec-ed commands.

This changes things so that ubi-command-execute still specifies the
command, but the arguments are passed in the response body,
separated by "\0".  The client checks each argument for validity:

* At least one argument must be "--"
* At most one argument not already present in argv is allowed
* The argument not already present in argv must be after "--"

So the worst a malicious server can do is rearrange argument
order and insert a single new argument, which will be parsed
as an argument and not as an option.  That's about the same
security as before, with a lot less complexity and a lot more
flexibility.

Use the new flexibility to support passing options to the
vm ssh, vm sftp, and vm scp commands:

  vm ssh location vm-name -A --
  vm sftp location vm-name -A
  vm scp location vm-name local-path :remote-path -A

For vm ssh, the -- is required to separate ssh options from
ssh arguments.

In addition to making the code significantly less complex,
this makes the specs much more understandable.

To ease debugging of bin/ubi, add support for an UBI_DEBUG
environment variable, which will print the arguments passed
to Process.exec, so you can more easily confirm correct
behavior.
2025-02-05 11:01:58 -08:00

19 lines
493 B
Ruby

# frozen_string_literal: true
UbiRodish.on("vm", "ssh") do
options("ubi vm ssh [options] location-name (vm-name|_vm-ubid) [ssh-options --] [cmd [arg, ...]]", key: :vm_ssh, &UbiCli::SSHISH_OPTS)
args(2...)
run do |(location, name, *argv), opts|
handle_ssh(location, name, opts) do |user:, address:|
if (i = argv.index("--"))
options = argv[0...i]
argv = argv[(i + 1)...]
end
["ssh", *options, "--", "#{user}@#{address}", *argv]
end
end
end