12

I'm automating a deploy process and I want to be able to just call one .sh file on my machine, have it do my build and upload the .zip to the server and then do a bunch of stuff on the server. One of the things I need to do requires me to be root. So, What I want to do is this:

ssh user@172.1.1.101 <<END_SCRIPT
su - 
#password... somehow...
#stop jboss
service server_instance stop
#a bunch of stuff here
#all done!
exit
END_SCRIPT

Is this even possible?

cmcculloh
  • 979
  • 2
  • 8
  • 14

8 Answers8

15

Have you considered a password-less sudo instead?

medina
  • 1,970
  • 10
  • 7
  • 1
    This is the approach I've used in the past. You can limit sudo to the required commands (start/stop the service), limiting the privileges the user has to precisely what you need. It still feels clunky, but works. – Mark Jul 15 '10 at 03:19
  • This is actually what I ended up doing... – cmcculloh Aug 11 '10 at 16:10
5

Instead of su, use sudo with the NOPASSWD set in sudoers for appropriate command(s). You'll want to make the allowed command set as limited as possible. Having it call run a script for the root commands may be the cleanest way and by far easiest to make secure if you are unfamiliar with sudoer file syntax. For commands/scripts that require the full environment to be loaded, sudo su - -c command works although may be overkill.

Jeremy M
  • 819
  • 4
  • 10
3

What you're describing might be possible with expect.

Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
3

You can pass a command as an argument to SSH to just run that command on the server, and then exit:

ssh user@host "command to run"

This also works for a list of multiple commands:

ssh user@host "command1; command2; command3"

Or alternatively:

ssh user@host "
        command1
        command2
        command3
"

As other users have pointed out before me, running su on the server will launch a new shell instead of executing subsequent commands in the script as root. What you need to do, is to use either sudo or su -c, and force TTY allocation to SSH with the -t switch (required if you need to enter root password):

ssh -t user@host 'su - -c "command"'
ssh -t user@host 'sudo command'

To sum it all up, one way of accomplishing what you want to do, would be:

#!/bin/bash
ssh -t user@172.1.1.101 "
        sudo some_command
        sudo service server_instance stop
        sudo some_other_command
"

Since sudo typically remembers you authorization level for a few minutes before asking for the root password again, just prepending sudo to all commands you need to run as root is likely the easiest way to run commands as root on the server. Adding a NOPASSWD rule for your user in /etc/sudoers would make the process even smoother.

I hope this helps :-)

jabirali
  • 191
  • 4
  • If you need more information about adding that `NOPASSWD` rule, check the examples at the bottom of `man sudoers`. Also, remember to use `visudo` when editing your `sudoers` file to avoid breakage! – jabirali Jul 15 '10 at 11:07
  • I can't get the su - -c "command" to work. How would I supply the password for su? – cmcculloh Jul 16 '10 at 02:52
  • If you choose to use `su -c` instead of `sudo`, you should run SSH with the `-t` flag - you will be prompted for the root password on the server when the command is executed. Providing a password directly to `su` as a command line argument is neither supported (as far I know), nor recommended - since a simple `ps -ef | grep su` reveals all arguments passed to `su`, including any password provided from the commandline... – jabirali Jul 17 '10 at 15:31
  • As for how you invoke `su -c` over SSH: `ssh -t user@host 'su -lc "`
    `echo this is a test `
    `echo this is another test`
    `"`
    `'`
    – jabirali Jul 17 '10 at 15:33
  • Replace the `
    ` above with newlines in your script. **Off-topic:** Can you add newlines to ServerFault comments? That would be quite useful when posting code snippets...
    – jabirali Jul 17 '10 at 15:36
2

No. Even if you get the password to su, you still have to deal with the fact that su will open a new shell, which means that your further commands will not be passed to it. You will need to write a script for the root operations along with a helper executable that can invoke it with the appropriate privileges.

Ignacio Vazquez-Abrams
  • 45,019
  • 5
  • 78
  • 84
  • 2
    Most su commands accept the -c option which will take as an argument the command(s) to run. I agree that a script on the server itself is probably best vs. sending all the cmds over the ssh. – Aaron Bush Jul 15 '10 at 00:48
1

Write an expect script. I know you've already found an answer for this, but this might be helpful if you have to login to a bunch of servers that require interactive password entry.

It's a language based off of TCL, so it might be a bit weird compared to plain old shell scripts. But it handles automation of text input and, you guessed it, "expects" certain bits of output before entering in any sort of automated password entry or escalation of privileges.

Here's a good link as a guide: http://bash.cyberciti.biz/security/expect-ssh-login-script/

Just in case the site goes down:

#!/usr/bin/expect -f
# Expect script to supply root/admin password for remote ssh server
# and execute command.
# This script needs three argument to(s) connect to remote server:
# password = Password of remote UNIX server, for root user.
# ipaddr = IP Addreess of remote UNIX server, no hostname
# scriptname = Path to remote script which will execute on remote server
# For example:
#  ./sshlogin.exp password 192.168.1.11 who
# ------------------------------------------------------------------------
# Copyright (c) 2004 nixCraft project <http://cyberciti.biz/fb/>
# This script is licensed under GNU GPL version 2.0 or above
# -------------------------------------------------------------------------
# This script is part of nixCraft shell script collection (NSSC)
# Visit http://bash.cyberciti.biz/ for more information.
# ----------------------------------------------------------------------
# set Variables
set password [lrange $argv 0 0]
set ipaddr [lrange $argv 1 1]
set scriptname [lrange $argv 2 2]
set arg1 [lrange $argv 3 3]
set timeout -1
# now connect to remote UNIX box (ipaddr) with given script to execute
spawn ssh root@$ipaddr $scriptname $arg1
match_max 100000
# Look for passwod prompt
expect "*?assword:*"
# Send password aka $password
send -- "$password\r"
# send blank line (\r) to make sure we get back to gui
send -- "\r"
expect eof
1

I know this is a bit late but I would personally use Net::SSH::Expect, available from CPAN.

http://search.cpan.org/~bnegrao/Net-SSH-Expect-1.09/lib/Net/SSH/Expect.pod

misconfig
  • 11
  • 1
  • Expect is really nice in many of those cases if you can't use regular ssh/bash scripting. However, it also becomes really clunky quickly and makes you assume "whatever you find" on the remote end. If you can change the target system or influence how it behaves *at all* I would recommend trying a different route first. Good point to bring it up here, though. – Theuni Dec 08 '12 at 23:28
0

You can use 'ssh example.com su -lc "$cmd"'.

When command contains options, you need to quote it, otherwise "su" might eat them ("su: invalid option -- 'A').

ssh -AX -tt example.com 'su -lc "tmux new-session -A"'
blueyed
  • 723
  • 8
  • 13