0

o's!

Maybe you can help me with this. I can't find an answer to my specific questions, because there is an obvious solution which I'm not allowed to use. But first things first, the context:

In my company, which is a service provider, we administrate a bunch of Linux servers. Some of my colleagues has for a long time been running a BASH script from a source server, that then performs some tasks over SSH on a number of remote Linux servers. The tasks it performs has to be executed as root, so what the script does is it authorizes the source server as root on the remote Linux servers via SSH (the remote servers has the source servers public SSH key). Then what happened is a new security policy was enforced and now root login over SSH is denied. So the mentioned method no longer works.

The solution I keep finding, which we are by policy not allowed to do, is to create an entry in the sudoers file allowing sudo to root without password for the specific user. This is the terms and they have to obey that. The only procedure that is allowed is to log on to the target server with your personal user, and then sudo su - to root WITH password. Cocky as I apparently was, I said, "It should be possible to have the script do that automatically", and the management was like "Cool, you do it then!" and now I'm here at Stack Overflow, because I know this is where bright minds are.

So this is exactly what I want to do with a BASH script, and I do not know if it's possible or how it's done, I really hope you can help me out:

Imagine Bob, he's logged into the source server, and he wants to execute the script against a target server. Knowing that root over SSH doesn't work, the authorization part of the script has been upgraded. When Bob runs the script, it prompts him for his password. The password is then stored in a variable (encrypted would be amazing) and the script then logs on the target server as his user (which is allowed) and then automatically elevates him to root on the target server using the password he entered on the source server. Now the script is root and it runs its tasks as usual.

elevate over SSH automatically

Can it be done with BASH? and how?

UPDATE:

The Script:

    ## define code to be run on the remote system
remote_script='sudo -S hostname'

## local system

# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password

# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh -t 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")

The error:

    [worker@source ~]$ sh elevate.sh 
Enter password for : abc123
elevate.sh: line 10: syntax error near unexpected token `<'
elevate.sh: line 10: `ssh -t 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")'
Raker
  • 344
  • 2
  • 6
  • 15
  • So you want to enter a password on the remote system that you retrieved locally? Yes, it can be done, but not with pure bash -- you'll want something like `expect` that can emulate a TTY. – Charles Duffy May 10 '16 at 17:21
  • ...however, the "secure variable" part is tricky: bash has no support for mlock, so you can't possibly make that variable storage as secure as best-practices would have it. – Charles Duffy May 10 '16 at 17:22
  • Important: **never** pass passwords in an environment variable or a command line. (Shell variables are necessary if you really mean pure bash, environment variables are not). – Charles Duffy May 10 '16 at 17:23
  • Related: http://stackoverflow.com/questions/233217/how-to-pass-the-password-to-su-sudo-ssh-from-the-command-line-instead-of-stdin – Charles Duffy May 10 '16 at 17:26
  • Hi Charles - yes I want to enter a password locally and have the script use that password to authorize on a remote server. I guess the initial login part, from the user can be done "without" password, by using ssh keys, but the second part. Elevating to root on the remote server, this is where I'm stuck. Keeping in mind I can't do the "user all=(all) nopasswd: all" thing. – Raker May 10 '16 at 17:26
  • Yes -- see the almost-a-duplicate question I linked, which covers that part you're having trouble with. BTW, I strongly suggest showing your implementation for code review when you have one together -- it's very, very easy to get the details wrong in a security-impacting way. – Charles Duffy May 10 '16 at 17:27
  • ...`expect` (which is, mind you, an extension to the TCL language, not part of bash, so any answer using expect is technically noncompliant with the "with bash" part of your question), which I described above, will *also* let you script password entry. The OP in the other question couldn't use it because it wasn't installed, but that may not be the case for you. – Charles Duffy May 10 '16 at 17:28
  • Expect would only be necessary on the source server? – Raker May 10 '16 at 17:31
  • You could certainly structure it that way, yes. – Charles Duffy May 10 '16 at 17:31
  • Assuming you stored Bob's password to shell variable `password`. You can invoke `ssh user@host "sudo -S 'remote commands'" <<< "$password"`. The option `-S` tells `sudo` to read password from `stdin`. – alvits May 10 '16 at 18:44
  • @alvits, ...you're right -- I significantly overcomplicated things here. Cleaned up my answer appropriately. – Charles Duffy May 10 '16 at 18:45
  • @alvits, ...that said, `<<<` is the Wrong Tool since on many platforms herestrings become temporary files stored on disks; you don't want your password in `/tmp/sh-thd-12356`. Thus, `< <(printf '%s' "$password")` is actually significantly more secure. – Charles Duffy May 10 '16 at 18:47
  • @CharlesDuffy - thanks for the tip. You'll notice that the herestring in my comment is running on the local host, which usually is a laptop or a personal computer. Your suggestion is, however, more appropriate if the local host is shared or remotely accessible. – alvits May 10 '16 at 18:55
  • @CharlesDuffy - The OP should go with your suggestion of using `< <(printf '%s' "$password)` for security's sake though. – alvits May 10 '16 at 19:01
  • Useful dialogue you two just had there. I learned a great deal from that knowledge exchange. Thanks to both – Raker May 10 '16 at 19:24
  • 1
    @alvits, ...I'm not sure I can concur that a system being a laptop or private machine negates the best practice against letting unencrypted keying data hit disk -- an admin or developer's personal machine is a tempting compromise target, after all, and attacks against development and administration tools are not unheard of. (See https://git-blame.blogspot.com.es/2014/12/git-1856-195-205-214-and-221-and.html, for instance -- having malicious git hooks installed is a fast route to arbitrary code execution). – Charles Duffy May 10 '16 at 19:42
  • @CharlesDuffy - you've made a very solid point. I don't have any argument about it. – alvits May 10 '16 at 20:41
  • @Raker, you'll note that it was `< <(...)` both in my answer and in every comment in this discussion where the syntax was mentioned. Why did you change it to `<< (...)`? (And if you're going to change things and then get an error, perhaps you might try the original before complaining about it?) – Charles Duffy May 11 '16 at 15:56
  • Now you're using `sh elevate.sh`; it **must be** `bash elevate.sh`. Your question is tagged `bash`, not `sh`; you should thus expect that an answer will work only with the shell you asked it be written for. – Charles Duffy May 11 '16 at 15:57
  • (this is also part of why using `.sh` extensions for bash scripts is misleading, and generally poor practice: It misleads about which shells that code is compatible with. In general, for an executable script rather than a shell library, the ideal practice is not to use any extension at all, and let the shebang choose an interpreter automatically; that way that shebang can be changed if the code is rewritten to a different interpreter/language and callers need not change). – Charles Duffy May 11 '16 at 15:59
  • True, makes perfect sense. Guess I'm just used to use sh without ever thinking about why. Thanks for the lesson. – Raker May 11 '16 at 16:01
  • Now I get a new error. sudo: that i need tty to run sudo, then I add -t to the command, like you see in the code example "ssh -t ..." and then it gives the error: "Pseudo-terminal will not be allocated because stdin is not a terminal." – Raker May 11 '16 at 16:03
  • `ssh -t -t`; you need to use it twice to force allocation in that case. Or you could edit `/etc/sudoers` to allow use without a TTY. Or you could wrap it with `unbuffer` (a helper that ships with `expect`), which will simulate a TTY. – Charles Duffy May 11 '16 at 20:28

3 Answers3

2

First: Because it exposes plaintext passwords to the remote system (where they can be read by an attacker using diagnostic tools such as strace or sysdig), this is less secure than correctly using the NOPASSWD: flag in sudoers. If your security team aren't absolute idiots, they'll approve a policy exemption (perhaps with some appropriate controls, such as having a dedicated account with access to a setuid binary specific to the command being run, with authentication to that account being performed via public key authentication w/ the private key stored encrypted) rather than approving use of this hack.

Second: Here's your hack.

## define code to be run on the remote system
remote_script='sudo -S remote_command_here'

## local system

# on the local machine: prompt the user for the password    
read -r -p "Enter password for $host: " password

# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh "$host" "$remote_script" < <(printf '%s\0' "$password")
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Awesome Charles. I will try it out in my test environment and see if I can make it work. I'll do my best to create a test script that logs on to a server and creates an empty file (owner should show root if i'm correctly elevated). I will post my results (or issues I'll probably run into a couple). Thanks a lot for your input, I really appreciate it. – Raker May 10 '16 at 18:19
  • Security team are 'somehow' idiots, because they can say what is insecure, but not how to make it secure + working. I mean they don't approve the nopasswd option for personal users, which is fine, but a service user is not allowed as well, so no nopasswd option there neither. They're like: "Cloooose the holes! (which i understand and appreciate) but they have no idea how to redesign the solutions, as I said. And they probably wont like the strace / sysdig part you mentioned. They'll probably smash this because of that risk. Guess the "secure variable" is more important than I initially thought – Raker May 10 '16 at 18:31
  • sysdig isn't a very well-known tool, and there are available mitigations against strace of setuid binaries (such as sudo), so there's a chance they'll let this fly. Though if I were them, I wouldn't. :) – Charles Duffy May 10 '16 at 18:43
  • ...btw, I had a bug here earlier, using `%q` in a scenario where `%s` was appropriate. See edit history. – Charles Duffy May 10 '16 at 18:44
  • I'm trying to make it work, first by having the script execute a simple command like "hostname". I have updated the original post with the script, which is most of all a copy paste of your post, with a few modifications. – Raker May 11 '16 at 15:46
  • I'm not seeing that update. That said, consider adding your own answer -- that way it can be voted on, edited, accepted/unaccepted, &c. independently of the question; no objection to copying code so long as credit is given. – Charles Duffy May 11 '16 at 15:48
  • Sorry, it's there now. – Raker May 11 '16 at 15:52
  • When I wrote `< <(...)`, I meant that, not `<< (...)`. Deliberate syntax choice, not a typo. – Charles Duffy May 11 '16 at 15:53
  • @Raker, ...see http://mywiki.wooledge.org/ProcessSubstitution; note that if the exact syntax I gave still doesn't work, it may be that you're using `#!/bin/sh`, rather than `#!/bin/bash`. – Charles Duffy May 11 '16 at 15:54
0

Allright, this is not the final answer, but I think I'm getting close, with the great help of CharlesDuffy.

So far I can run the script without errors on a remote server, that already has the publickey of my source server. However the command I execute doesn't create a file as I tell it to on the remote system.

However the script seems to run and the password seems to be accepted by the remote system.

Also I have to change in the sudoers on the remote host the line "Defaults requiretty" to "Defaults !requiretty", else it will tell me that I need a TTY to run sudo.

#!/bin/bash

## define code to be run on the remote system
remote_script='sudo -S touch /elevatedfile'

## local system

# on the local machine: prompt the user for the password
read -r -p "Enter password for $host: " password

# ...and write the password, followed by a NUL delimiter, to stdin of ssh
ssh -T 10.0.1.40 "$remote_script" < <(printf '%s\0' "$password")

UPDATE: When I tail /var/log/secure on the remote host I get the following after executing the script, which seems like the password is not being accepted.

May 11 20:15:20 target sudo: pam_unix(sudo:auth): conversation failed
May 11 20:15:20 target sudo: pam_unix(sudo:auth): auth could not identify password for [worker]
May 11 20:15:20 target sshd[3634]: Received disconnect from 10.0.1.39: 11: disconnected by user
May 11 20:15:20 target sshd[3631]: pam_unix(sshd:session): session closed for user worker

What I see on the source server, from where I launch the script:

[worker@source ~]$ bash elevate.sh 
Enter password for : abc123
[sudo] password for worker: 
[worker@source ~]$
Raker
  • 344
  • 2
  • 6
  • 15
0

Just make a daemon or cron script running as root, that in turn will check for any new scripts in specified secure location (ie. DB that it only has READ access to), and if they exist, it will download and execute them.

skye
  • 1