capistrano/sshkit

whoami and id -a return different users

bretweinraub opened this issue · 16 comments

    def test
      on(ip_address) do
        as(:deploy) do
          puts capture "whoami"
          puts capture "id -a"
        end
     end 
    end

returns

deploy
uid=500(different user) gid=100(users) groups=100(users),1003(rvm)

it's a mystery; and the net effect is that commands with "as" blocks are failed with insufficient permissions.

Ruby 2.6.5 OSx
sshkit version: 1.21.0

What OS are you connecting to ?

Ubuntu 20.04 LTS.

Is there a way I can configure which user I connect as? I'm guessing "as()" does a sudo, so if I can change the user that is used in the SSH connection, I can try to isolate any issues related to login shell.

You can change that in the SSH options:

I am sure you have seen the docs already, but I'd draw your attention to https://capistranorb.com/documentation/faq/why-does-something-work-in-my-ssh-session-but-not-in-capistrano/ as you seem to be familiar with the concept of login/non-login shells.

Hmmm, that helped, there's precious little in the login shell of this user. Problem persisted

        SSHKit::Backend::Netssh.configure do |ssh|
          ssh.connection_timeout = 30
          ssh.ssh_options = {
            user: 'deploy'
          }
        end        
        on(ip_address) do
          as(:root) do
            puts capture "whoami"
            puts capture "id -a"
          end
        end

root
uid=1000(deploy) gid=1002(deploy) groups=1002(deploy),1003(rvm)

        on(ip_address) do
          as(:root) do
            puts capture "whoami"
            puts capture "id -a"
            puts capture("[[ $- == *i* ]] && echo 'Interactive' || echo 'Not interactive'")
            puts capture("shopt -q login_shell && echo 'Login shell' || echo 'Not login shell'")            
            # puts capture "whoami"
            # puts capture "ifconfig"
          end
        end
root
uid=1000(deploy) gid=1002(deploy) groups=1002(deploy),1003(rvm)
Not interactive
Not login shell

Note I see similar behavior with "within()", I can run a pwd, and it shows correctly, but if I execute a command [like touching a file], it appears in my home directory on the target machine. Not user dependent, persists on the 'deploy' user which is really vanilla [as opposed to my account]. This machine is a fresh build.

If you crank up the logging to extra verbose, what commands are we trying to send? https://github.com/capistrano/sshkit#verbosity--silence

OK this block:

      SSHKit::Backend::Netssh.configure do |ssh|
        ssh.connection_timeout = 30
        ssh.ssh_options = {
          user: 'deploy'
        }
        SSHKit.config.output_verbosity= Logger::DEBUG
      end        
      on(ip_address) do
        as(:root) do
          within('/tmp') do 
            puts capture "id -a", verbosity: :ERROR
            puts capture "whoami", verbosity: :ERROR
          end
        end
      end

Generates:

 DEBUG [f8e40a33] Running if ! sudo -u root whoami > /dev/null; then echo "You cannot switch to user 'root' using sudo, please check the sudoers file" 1>&2; false; fi on xxx.xxx.xxx.xxx
 DEBUG [f8e40a33] Command: if ! sudo -u root whoami > /dev/null; then echo "You cannot switch to user 'root' using sudo, please check the sudoers file" 1>&2; false; fi
 DEBUG [f8e40a33] Finished in 1.218 seconds with exit status 0 (successful).
 DEBUG [be32fd96] Running if test ! -d /tmp; then echo "Directory does not exist '/tmp'" 1>&2; false; fi on 172.105.97.254
 DEBUG [be32fd96] Command: if test ! -d /tmp; then echo "Directory does not exist '/tmp'" 1>&2; false; fi
 DEBUG [be32fd96] Finished in 0.150 seconds with exit status 0 (successful).
 ERROR [22793598] Running id -a on xxx.xxx.xxx.xxx
 DEBUG [22793598] Command: id -a
 DEBUG [22793598] 	uid=1000(deploy) gid=1002(deploy) groups=1002(deploy),1003(rvm)
 ERROR [22793598] Finished in 0.190 seconds with exit status 0 (successful).
uid=1000(deploy) gid=1002(deploy) groups=1002(deploy),1003(rvm)
 ERROR [68fe33a9] Running /usr/bin/env whoami on xxx.xxx.xxx.xxx
 DEBUG [68fe33a9] Command: cd /tmp && sudo -u root -- sh -c /usr/bin/env\ whoami
 DEBUG [68fe33a9] 	root
 ERROR [68fe33a9] Finished in 0.149 seconds with exit status 0 (successful).

pretty weird. Where in the code does the command string get built? I'll just rig up the debugger for this spot and see if what's gone amiss with the decision making process.....

What happens if you change it to:

puts capture(:id, "-a")

SSHKit behaves differently if you pass it a single command string containing spaces vs a list of arguments.

def should_map?
!command.match(/\s/)
end

What happens if you change it to:

puts capture(:id, "-a")

SSHKit behaves differently if you pass it a single command string containing spaces vs a list of arguments.

def should_map?
!command.match(/\s/)
end

Yeah, that seems to be the difference. By my reading of the code, if the command contains whitespace, it is just executed, outside of any mapping.

So I wonder which is "correct", the code or the documentation.

https://github.com/capistrano/sshkit#running-commands

The documentation reads:

Note: In SSHKit, the first parameter of the execute / test / capture methods has a special significance. If the first parameter isn't a Symbol, SSHKit assumes that you want to execute the raw command and the as / within / with methods, SSHKit.config.umask and the comand map have no effect.

But in actual fact, it's not that it is a String, it is that contains some whitespace.

But many thanks, mysteries are revealed!

Good catch @bretweinraub sorry, I might have noticed that on the first few posts you made. I'm glad you have it working now.

To me this would be more clear to be in the options.

execute("id -a"), without_mapping: true

as this sort of violates the principle of least surprise.....

I went to update the README.md file, and it is pretty hard to explain.

Hi Lee -

Yeah I suspected that this is baked into all kinds of capistrano now.....

Just a ? about this:

def should_map?
!command.match(/\s/)
end

So is 'command' in that piece of code just the first argument, or is it the whole concatenated list of strings?

For example

execute(:sym,"-f","arg1 -a arg2")

Arg1 is a sym, but there's whitespace in there too in the 3rd argument. Is it mapped or not?