su without password within a script - security

I'm sure this question has been answered before, but I can't find an answer that I like. I would like to write a shell script that executes a very specific script as another user (I want anyone to be able to start postgres as the postgres user). The script will have 710 perms so it will be executable by a certain group but not readable or writable by that group.
Now, I'm pretty sure there's no way to use 'su' without an interactive password prompt. There are lots of good reasons for that and I don't need to be convinced of the merit of those reasons (I'm told that someone savvier than me could grab the password off the processes list which is bad).
Question is, more generally how would I accomplish what I want to do without abusing unix security paradigms? Is there a way to allow user to execute a very specific process as another user?

This sort of situation is exactly what sudo was designed for.

You can create an executable (not a shell script) that launches the script that should run as the postgres user. Change the owner of the executable to the postgres user, and set the setuid bit.
See Best practice to run Linux service as a different user to address Celada's concern.

Well, you could use a simple script to access programmatically to an user using sudo and then execute all code you want.
Here is a simple script:
if [ "$#" -ne 2 ]; then
echo "Usage: "
echo " suprompt <user> <password>"
else
echo $2 | sudo -sS su $1
sudo su $1
fi
This script uses two arguments. The first one is the user you want to be, and the second arg is the password.
It works automatically.
You can change the final statement and do: sudo su $1 -c <command>
I hope this will work for you.

Related

Linux: Checking if a user has a shell or not

I'm writing a test script in python where I use subprocess to run various terminal commands and check the result. One of the things I want to check is if the user "games" doesn't have a shell. I don't want to log in as games(which I think is impossible anyway), but have the ability to run this command as root. Is there any single bash command I can use to check what shells another user has(or doesn't have)?
I'm able to use the command "cat /etc/shells/" to check what shells I have available, I wanted to use this to search another user but I'm not sure how to do it, if it's even possible.
You may use "su", and check the return code:
root#shinwey:# su games
This account is currently not available.
root#pifa:/home/kalou/t# echo $?
1
or print the string "NO_SHELL":
root#shinwey:# su games 2>&1 > /dev/null || echo NO_SHELL
NO_SHELL

How can I ingore whitespaces inside an echo command when usual methods don't seem to work

Hi I'm pretty new to linux/bash in general and I'm having a some trouble making a script for my coworker. Idea of this script is to help automate coworkers IP table entries (don't use IP tables myself so no idea how it works, just working as per his instructions). Program is going to ask a few questions, form an entry and then add it to a file on a different host. After the file is written it will also run
"systemctl reload iptables.service" and "systemctl status iptables". I tested that pwd was atleast working where I was planning to put these.
The code worked fine with a single word in place of table_entry variable, I was able to write something to a file in my host computer with a different user.
Problem is that the "table_entry" variable is going to have whitespaces in it and the sudo su -c command gets the second word as input (atleast that's what I think is happening) and I get an error sudo: INPUT: command not found. the "INPUT" coming from my case sentence
I tried to put the "table_entry" variable in "{$table_entry}" , "$table_entry" and {$table_entry} forms but they didn't work.
I removed some of the code to make it more readable (mostly case sentences for variables and asking for host and username).
#!/bin/bash
echo -e "Which ports?
1. INPUT
2. OUTPUT
3. Forward"
read opt_ch
case $opt_ch in
1) chain="INPUT" ;;
2) chain="OUTPUT" ;;
3) chain="FORWARD" ;;
*) echo -e "Wrong Option Selected!!!"
esac
table_entry="-A $chain "#-s $ip_source -d $ip_dest
ssh -t user#host "sudo table_entry=$table_entry su -c 'echo $table_entry >> /home/user/y.txt'"
#^ this line will later also include the systemctl commands separated with ";" ^
I tested few different methods how to do this script overall, heredoc(didn't get input to work very well), Ansible(Didn't really seem like a great tool for this job), Python(can't install new modules to the enviroment). So this is the best solution I came up with bearing my limited skillset.
Edit: I also realise this is propably not the smartest way to do this script, but it's the only one I have gotten to work this far, that can also ask for a password from the user when doing su command. I'm not knowledgeable in transferring passwords safely in linux enviroment or in general, so I like to let linux handle the passwords for me.
This a problem with dealing with nested quoting - which is really quite the annoying problem to solve.
This case seems like you could do this with quotes inside the string - your example would become
ssh -t user#host "sudo table_entry='$table_entry' su -c 'echo \"$table_entry\" >> /home/user/y.txt'"
It seems to me the table_entry='$table_entry' is redundant though, this should work:
ssh -t user#host "sudo su -c 'echo \"$table_entry\" >> /home/user/y.txt'"
Your comment (denoted with #) is getting concatenated with the table_entry string you're trying to form. Try adding a space like this:
table_entry="-A $chain " #-s $ip_source -d $ip_dest
Then table_entry gets assigned correctly. I was using KWrite to edit your bash script, and it does text highlighting that quickly showed me the problem.

How do I redirect string input into sudo?

I'm writing a bash script that requires root access and so what I need to do is write something like
$my_psswd >> sudo some_command parameter1 parameter2
to automate the process. I'm not concerned if this opens up security holes. This is more or less of an example that I can think of. But the problem is that when I initiate sudo anything, it asks for user input which I'm not sure how automate or provide as a variable.
I've tried things like
$my_psswd >1 sudo something
echo $my_psswd | sudo something
but none of this is what I want. Also this has to be a bash script, I can't use a program like expect. Thanks.
You need -S switch for sudo command:
-S The -S (stdin) option causes sudo to read the password from the standard input instead of the terminal device. The password must be followed by a newline character
I recommend against doing this at all. You will be storing your password in plain text which means other people may have access to it. Or it will be visible in process listings, again available to other users.
There are a couple of alternatives to prevent this:
Run the entire script using sudo. Do not use sudo in the script itself but run the entire things with elevated privileges. The downside is off course that your might be executing things with elevated privileges that do not require it but with no more background that's impossible to say.
Better would be to configure your account to execute those specific commands through sudo without providing a password. That way you can execute only the commands that need it with elevated prvileges without the problem of providing a password.
A workaround could be to run sudo -l before calling the script. That way sudo will have an active session and won't prompt for a password. This is only a workaround and would fail if one of the commands takes longer to execute than the configured grace time for sudo. But in small scripts this might be an easy fix.
Try echo $PW | sudo -S cmd
It may work for you.
you are not suppose to use sudo this way, use visudo to specify what commands are allowed to what users, then you don't need to worry about passwords.

Automatically invoking gksudo like UAC

This is about me being stressed by playing the game "type a command and remember to prepend sudo or your fingers will get slapped".
I am wondering if it is possible somehow to configure my Linux system or shell such that when I forget to type e.g. "sudo apt-get install emacs", instead of just telling me that I did something wrong, gksudo would get launched, allowing me to acknowledge my credentials and get on moving. Just like UAC does on windows.
Googling hasn't helped me yet..
So is this possible? Did I miss something? Or am I asking for a square circle?
Edit 2010 July 25th: Thanks everyone for your interrest. Unfortunately, Daenyth and bmargulies answers and explanations are what I anticipated/feared since it was impossible for me to google-up a solution prior to submitting this question. I hope that some nice person will someday provide an effective solution for this.
BR,
Christian
Linux doesn't allow for this. Unlike Windows, where any program can launch a dialog box, and UAC is in the kernel, Linux programs aren't necessarily GUI-capable, and sudo is not, in this sense, in the kernel. A program cannot make a call to elevate privilege (unless it was launched with privilege to begin with and intentionally setuid'd down). sudo is a separate executable with setuid privilege, which checks for permission. If it likes what it sees, it forks the shell to execute the command line. This can't be turned inside out.
As suggested in other posts, you may be able to come up with some 'shell game' to arrange to run sudo for you for some enumerated list of commands, but that's all you are going to get.
You can do what you want with a preexec hook function, similar to the command-not-found package.
There's no way to do this given the current linux software stack. Additionally, MS has a patent on this behavior -- present a user interface identifying an account having a right to permit a task in response to the task being prohibited based on a user's current account not having that right.
I don't think this really works in a general way (automatically deciding which application needs admin rights). However you could make aliases like this for every application:
alias alias apt-get='gksudo apt-get'
If you now enter apt-get install firefox the gnome asks for the admin password. You can store the commands in ~./bashrc
You could use a shell script like the following:
#!/bin/bash
$#
if [ $? -ne 0 ]; then
sudo $# # or "gksudo $#"
fi
This will run a command given in the arguments with a sudo prefix if the command came back with a non-zero return code (i.e. if it failed).
Use it as in "SCRIPT_NAME apt-get install emacs" for example. You may save it somewhere in your $PATH and set it as an alias like this (if you saved it as do_sudo):
alias apt-get='do_sudo apt-get'
Edit: That does not work for programs like synaptic which do work for non-root users but will give them less privileges. However, if the application fails when invoked without root privileges (like apt-get does) this works fine.
In the case where you want to always run a command as root but might already be root, you can solve this by wrapping a little bash script around it:
#!/bin/bash
if [ $EUID = 0 ]; then
"$#"
else
gksudo "$#"
fi
If you call this something like alwaysroot.bash and place it in the right spot on your PATH, then you can call your other program like this:
alwaysroot.bash otherprogram -arguments...
It even handles arguments with spaces in correctly.

Webapp update shell script

I feel silly asking this...
I am not an expert on shell scripting, but I am finally in enough of a sysadmin role that I want to do this correctly.
I have a production server that hosts a webapp. Here is my routine.
1 - ssh to server
2 - cd django_src/django_apps/team_proj
3 - svn update
4 - sudo /etc/init.d/apache2 restart
5 - logout
I want to create a shell script for steps 2,3,4.
I can do this, but it will be a very plain and simple bash script simply containing the actual commands I type at the command line.
My question: What is the best way to script this kind of repetitive procedure in bash (Linux, Ubuntu) for a remote server?
Thanks!
The best way is simply as you suggest. Some things you should do for your script would be:
put set -e at the top of the script (after the shebang). This will cause your script to stop if any of the commands fail. So if it cannot cd to the directory, it will not run svn update or restart apache. You can do this programmatically by putting || exit 0 after each command, but if that's all you're doing, you may as well use set -e
Use full paths in your script. Do not assume the directory that the script is run from. In this specific case, the cd command has a relative path. Use a full (absolute) path, or use an environment variable like $HOME.
You may want to set up sudo so that it can run the command without asking for a password. This makes your script non-interactive which means it can be run in the background and from cron jobs and such.
As time goes by, you may add features and take command line arguments to parameterise the script. But don't bother doing this up front. Just evolve your scripts as you need.
There is nothing wrong with a simple bash script simply containing the actual commands you type at the command line. Don't make it more complicated than necessary.
I'd setup a cron job doing that automatically.
Since you're using python, check out fabric - you can use it to automate these kind of tasks. First install fabric:
$ sudo easy_install fabric
then write your fabric script:
from __future__ import with_statement
from fabric.api import *
def svnupdate():
with cd('django_src/django_apps/team_proj'):
run('svn update')
sudo('/etc/init.d/apache2 restart')
Save as fabfile.py, then run using the fab command:
$ fab -H hostname svnupdate
Tell me that's not cool! :-)
you can do this with the shell (bash,ksh,zsh + ssh + tools), or programming languages such as Python,Perl(Ruby or PHP or Java) etc, basically a language that supports SSH protocol and operating system functions. The "best" one is the one that you are more comfortable and have knowledge in. If you are doing sysadmin, the shell is the closest thing you can use. Then after you have done your script, you can use the crontab (cron) , or the at command to schedule your task. check their man page for more information
You can easily do the above using bash/Bourne etc.
However I would take the time and effort to learn Perl (or some similarly powerful scripting language). Why ?
the language constructs are much more powerful
there are no end of libraries to interface to the systems/features you want to script
because of the library support, you won't have to spawn off different commands to achieve what you want (possibly valuable on a loaded system)
you can decompose frequently-used scripts into your own libraries for later use
I choose Perl particularly because it's been designed (perhaps designed is too strong a word for Perl) for these sort of tasks. However you may want to check out Ruby/Python or other suggestions from SO contributers.
For the basic steps look at camh's answer. If you plan to run the script via cron, then implement some simple logging, e.g. by appending start time of each command with exit code to a textfile which you can later analyze for failures of the script.
Expect -- scripting interactive applications
Expect is a tool for automating interactive applications such as telnet, ftp, passwd, fsck, rlogin, tip, etc.... Expect can make easy all sorts of tasks that are prohibitively difficult with anything else. You will find that Expect is an absolutely invaluable tool - using it, you will be able to automate tasks that you've never even thought of before - and you'll be able to do this automation quickly and easily.
http://expect.nist.gov
bonus: Your tax dollars at work!
I would probably do something like this...
project_update.sh
#!/bin/bash
#
# $1 - user#host
# $2 - project directory
[[ -z $1 || -z $2 ]] && { echo "usage: $(basename $0) user#host project_dir"; exit 1; }
declare host=$1 proj_dir=$2
ssh $host "cd $proj_dir;svn update;sudo /etc/init.d/apache2 restart" && echo "Success"
Just to add another tip - you should not give users access to some application in an unknown state. svn up might break during the update, users might see a page that's half-new half-old, etc. If you're deploying the whole application at once, I'd suggest doing svn export instead to a new directory and then either mv current old ; mv new current, or even keeping current as a link to the directory you're using now. Still not perfect and not blocking every possible race condition, but it definitely takes less time than svn up on the live copy.

Resources