'su' by using 'script' in Docker returns different results compared to the standard environment - linux

I need to request certain commands via su including password in one line.
I found a solution and it is working in a standard environment (Ubuntu) (more about solution here):
{ sleep 1; echo password; } | script -qc 'su -l user -c id' /dev/null | tail -n +2
But I am faced with the problem that this solution is not suitable in a Docker container environment
Script terminates the command without waiting for echo and as a result i get:
su: Authentication failure
Any help is much appreciated.

Passing the password for su via stdin is problematic for various reasons: the biggest one is probably that your password will end up in the history.
You could instead:
Call the entire script as the specific user and thus enter the password manually
Use sudo with the appropriate NOPASSWD sudoers configuration
In your case you are using docker, so you could just set the USER in your Dockerfile

Related

How can I pass a password to bash script

I'd like to write a bash script which automates a specific process.
It starts an analyzing cms-tool with passing an identifier.
After passing an identifier that tool is asking for user password.
The script should read through a list of such identifiers and input the user password after each forwarded identifier.
The following simple example shows the process with an identifier called 'eventDatePicker' :
jmm#workstation:/opt/media/tools/bin$ ./cm old-viewtypes-usage -u admin -s "/test/" -t "/home/media/transfer" -vt eventDatePicker
password:
This is my created bash script so far but I don't know how to implement a function for passing a user password:
#!/bin/bash
# list with identifiers
input="/opt/media/tools/bin/technical_identifier"
#path to analyzing tool
cd /opt/media/tools/bin || exit
while IFS= read -r line
do
./cm old-viewtypes-usage -u admin -s "/test/" -t "/home/media/transfer" -vt "$line"
# command for passing the user password
done < "$input"
I tried it out by using read or expect but it didn't work out.
I'd be glad for any help.
You might like to learn the 'expect' dialect of Tcl. Start with running 'autoexpect' and then change the output script to take parameters and send your password.
This is really the only sane way of scripting up interactive scripts.

How can I script a remote connection that is elevated via su?

I have scripts and programs which run commands on a remote computer, and I need some of the commands to be elevated, I.e. su. Since this is scripted, I cannot rely on a user to enter the password; it needs to be passed to su by the script.
I have tried a bunch of things, including echoing the password to su like so:
ssh user#host "echo password | su -c myCommand"
and
echo password | ssh user#host su -c myCommand
and in programming languages like C# creating a new process and reading from its stdout and writing the password to its stdin.
I've tried some alternate things others have mentioned online, but they generally involve commands that I do not have on some of the machines. Some of them have no expect or sudo or other alternatives others have mentioned.
How can I do this without installing any other tools?
The su tag says general su support is off topic, but this is not about su support or usage, it's specifically about a programming problem which requires elevated su use, so hopefully it's not taken as off topic. "Linux & Unix" and "Super User" users may be interested in this question too, but it really applies more to programming.
TL;DR: Use echo pass | ssh -t -t user#host 'su -c "command"'
This one gave me headaches and took a while to figure out. There are hints about the answer elsewhere, but I have not seen the answer stated explicitly anywhere, so it took a bit of playing around with to get right.
Using ssh
echo <password> | ssh -t -t <user>#<host> 'su -c "<command>"'
The quotes seem to be necessary. If I omitted one or the other pair of quotes I got incorrect results for some commands. For example, <…> ssh -t -t me#mypc su -c "ls -l /root/something_secure" would give me the ls contents of me's home directory, ignoring the -l and the /root/something_secure. So I had to have the quotes set up like that.
The -t is where the magic is at for our need, and yes you should put 2 of them. Doing echo pass | su -c command can fail because su doesn't take a password from standard input. Doing echo pass | ssh <…> su -c command seems like a good idea at first, because su needs to get input from ssh, and ssh gets it from standard input, but really su gets it from a "tty", not from standard input. The -t tells ssh to fake it by using a pseudo-tty which sends standard input to this fake tty. Sometimes ssh will complain and not want to allocate the pseudo-tty for you, so doing the -t twice tells ssh to shut up and do it anyway.
-t' Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful,
e.g. when implementing menu services. Multiple -t options force tty
allocation, even if ssh has no local tty.
From man ssh
Example:
echo MySecretPass | ssh -t -t me#mydevice 'su -c "chown me /some/file"'
Ssh does not have a password parameter. If you have not set up a password-less secure login, you should do so. Read up here. Otherwise, this answer will supply the password to su for you, but you would still have to supply the password to ssh for the initial ssh login.
Using plink (Putty command line ssh-like tool for Windows)
echo <password> | plink -t -t -pw <password> <user>#<host> "su -c '<command>'"
Again, the quotes seemed to be necessary. If I omitted them, the results were incorrect, similar as with using ssh.
Example:
echo RootPass | plink -t -t -pw MePass me#mydevice "su -c 'echo Rooty!> /root/rootiness'"
Reasoning is similar to the ssh section above.
Your favorite programming language
If your favorite programming language supports the ability to execute a shell command and control the standard input/output for it, then instead of piping an echo you can instead run the ssh or plink command above (minus the piped echo), then put the password into standard input.
So for C#
Process process = new Process();
// If I remember correctly, I think UseShellExecute needs to be false to redirect std in/out
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
// You can use either ssh or plink; I'll use plink here
process.StartInfo.FileName = "<path to plink>\\plink.exe";
process.StartInfo.Arguments = "-t -t -l " + username + " -pw " + password + " " + hostname + " 'su -c \"MyCommand\"'";
process.StartInfo.CreateNoWindow = true;
process.OutputDataReceived += new DataReceivedEventHandler(FunctionCallbackYouMake);
process.ErrorDataReceived += new DataReceivedEventHandler(AnotherFunctionYouMake);
// I'm not actually sure if this next line is necessary, but I've been using it because of an example I used
process.SynchronizingObject = null;
process.Start();
And then read the data coming in through the ssh (plink) connection by using the function callback you make and provide to OutputDataReceived. It has the form void f(object sending_process, DataReceivedEventArgs event)
You can write data to process.StandardInput, including the su password, such as
process.StandardInput.Write(password);
process.StandardInput.Flush();
I've done something similar in Java as well, so I know you can do pretty much the same thing there, but it's been so long I don't recall all the details, but it also involves a process object and setting up std in/out handlers. If I have time later I might include some of those details.
No matter what language or method you use, some programs may block if they get too much stuff in their output streams or error streams, so make sure that you register for both standard output and standard error even if you don't care about them, even if all you do is read their data and discard it.
But don't hard-code the password!
Since this is for programming, there is generally no need to hard-code passwords into scripts. You could if you absolutely had to, but preferably use variables place of the username, password, and host name so that you are more secure and your tool is more flexible.
For example, I have a line in Windows cmd which does the following
echo %password% | plink -t -t -pw %password% %username%#%ip% "su -c '<what I want to do goes here>'"
And the username, password, and ip are supplied as arguments to the cmd file. Also note the C# example above, they are variables.

Hide plaintext password from showing in bash script?

I have the following bash script to restart the network manager in Debian. The script works as is it should, but not as I would like it to. When the script asks for the sudo password I am able to pass it along using echo, but it displays the password in terminal while the script executes, making it less asthetically pleasing than I would like. Is there anyway to have the script enter the password, but not display the password text while the script calls for the sudo password?
I have tried as many suggestions on Stack Overflow as i could find, well as Stack Exchange before submitting this question.
Script is as follows:
#!/bin/bash
clear
echo "Restarting service Network Manager"
echo""
sleep 1
echo -e "\033[0;31m......................................\033[0m"
echo -e "\033[0;31m......................................\033[0m"
sleep 1
echo""
sudo service network-manager restart
sleep 2
echo <Password>
sleep 2
echo "Service Network Manager Restarted"
sleep 1
echo ""
echo "Relinquishing control of terminal to user..."
sleep 7
clear
Remove the echo <Password> line? I am pretty sure it does nothing other than display the password, as sudo apparently (through an appropriate entry in /etc/sudoers) works without you having to give a password. (What you write to terminal with echo does not get passed to any other process.)
Generally speaking, you can use sudo -S to make sudo expect the password on stdin. But also generally speaking, if you have to hardcode a password in a script, you're doing it wrong in some way.
Is there anyway to have the script enter the password
Putting password in script is not a good idea. First, from security point of view, password may be recovered from script from anyone with access to script. Second, from maintenance view, once you change your password, scripts suddenly stop working and you have to update them all.
Fortunately, as you are already using sudo there is better solution. You can configure sudo to allow running certain command without password, by using NOPASSWD rule in /etc/sudoers.
myuser ALL=(ALL) NOPASSWD: service network-manager restart
See:
How do I run specific sudo commands without a password?
How to run a specific program as root without a password prompt?
Warning: Always edit /etc/sudoers with visudo, never directly. It prevents you from breaking /etc/sudoers. Once you break your /etc/sudoers, you won't be able to use sudo, including using sudo to fix /etc/sudoers.
try this /bin/echo -e "password\n" | sudo apt-get update
or see this Use sudo with password as parameter

'su' command in Docker returns 'must be run from terminal'

I'm developing a docker environment for teaching purposes and need to be able to switch users inside docker.
I set up the 'user' user with a password but when I try to switch to it with su, I get "su must be run from terminal".
I get this if I try to ssh into the docker and also by issuing commands through a php shell (an apache service is running on the Docker instance).
Any help is much appreciated.
When you are ssh-ing in or going in via php your session is not being allocated a pty. I have used each of the following solutions:
ANSWER 1: use ssh -t or ssh -tt to get pty allocated when logging in using ssh:
I had great fun getting commands to run right due to ptys when running sessions like this: jenkins shell -> ssh driver -> ssh test -> docker exec.
Good answer here:
https://unix.stackexchange.com/questions/105422/command-must-be-run-from-a-terminal
"Try the -t option to ssh. If that does not work try -tt."
"-t Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty."
ANSWER 2: use docker run -t ... and docker exec -it
Use the -t and -it options to allocate pty in your docker exec session.
Also with docker exec you can simply use the -u option to login to container as different users and avoid using su. e.g.
$ docker exec -u root -it small_hypatia bash
There is a good question and answer on this here:
https://github.com/docker/docker/issues/8631
ANSWER 3: use python to spawn a pty in your shell
Quite a cute hack :)
jenkins#e9fbe94d4c89:~$ su -
su: must be run from a terminal
$ echo "import pty; pty.spawn('/bin/bash')" > /tmp/asdf.py
$ python /tmp/asdf.py
$ su -
Password:
root#e9fbe94d4c89:~#
This solution work by using 'script' command from the 'bsdutiles' package that setup a pty (a terminal). The 'sleep' command is there to prevent sending the password before the 'su' command is ready to read it. The 'tail' command remove the "Password:" input line issued by 'su'.
sh -c "sleep 1; echo rootpassword" | script -qc 'su -c whoami - root' | tail -n +2
Beware that the rootpassword could be see in many ways (history, ps, /proc/, etc...). Start the command with a space to at least avoid history recording.
If you use su-exec instead of su the issue with tty completely vanishes since it calls execvp directly instead of forking like su does.
Gosu is another similar alternative.

Most reliable way to identify the current user through a sudo

I have an application that may or may not be run while users are sudo'ed to a shared user account. I would like to reliably identify who the real user is for a sort of "honor-system" ACL. I think there's some way by tracing parent/group/session process ids the way that the pstree command does, but I'm not sure how to do that best or if there are better alternatives.
I tried getlogin() originally. That works if ./myapp is used, but it fails with 'cat input | ./myapp` (because the "controlling terminal" is a pipe owned by the shared account).
I'd rather not trust environment variables, as I don't want my "honor system" to be completely thwarted by a simply unset, when the information is still available elsewhere.
I'd also like to avoid forcing a lookup in the password database, as that is a remote RPC (NIS or LDAP) and I'm pretty sure wtmp already contains the information I need.
For a shell script, you might use this to get the sudo'ing user:
WHO=$(who am i | sed -e 's/ .*//'`)
and extract the id from the login using:
ID_WHO=$(id -u $WHO)
I'll ferret out the C library equivalent later.
sudo sets the environment variables SUDO_USER, SUDO_UID, and SUDO_GID.
You can test this with:
$ sudo env
[sudo] password for shteef:
TERM=xterm
# [...snip...]
SHELL=/bin/bash
LOGNAME=root
USER=root
USERNAME=root
SUDO_COMMAND=/usr/bin/env
SUDO_USER=shteef
SUDO_UID=1000
SUDO_GID=1000
But if your users have shell access on the shared account, then I suppose you cannot blindly trust this either.
How about:
#!/usr/bin/ksh
username=`id | cut -d"=" -f2 | cut -d" " -f1`
if [ $username == "0(root)" ]
then
print "Yes, the user is root"
else
print "Sorry! the user $username, is not a root"
fi

Resources