33

I have a script which can run as sudo script.sh or pkexec script.sh

It would be much nicer from the user point of view if the script asked for the password from the user when just running it by name script.sh.

How can I "embed" request to pkexec or sudo to run the whole script with root privilege?

Note that running everything with sudo sh -c might not be the best solution as I have functions in the script.

6 Answers 6

73
+100

This'll work:

echo "$(whoami)"

[ "$UID" -eq 0 ] || exec sudo "$0" "$@"

example:

./test.sh 
blade
[sudo] password for blade: 
root
6
  • 4
    What's good about this is recursion with exec - call itself but at the same time replacing process , so we don't spawn multiple instances of the script Mar 15, 2016 at 16:54
  • 2
    Recursion to the rescue!!! Remind me to add a bounty to this one in a few days!
    – Fabby
    Mar 16, 2016 at 0:30
  • 7
    Something to be aware of when doing this is privilege escalation; basically, the more commands you run as root, the more opportunities there are for a malicious hacker to exploit something to get access to a root account. I'm not saying it's always wrong to make the whole script run as root, but it's worth putting a bit of thought into it before you decide to do so. I think it would be nice (though not necessary) if the answer would touch on that.
    – David Z
    Mar 16, 2016 at 8:44
  • If you have a decent timeout value set in /etc/sudoers, then you can optionally put sudo in front of each command in the script which really needs root access and it will only ask once. One thing I often mess up is that calling such a script from a gui won't work because sudo gets /dev/null for password input and fails. That's what gksudo and kdesudo were designed to handle because they'll use the gui to ask for the password.
    – Joe
    Mar 16, 2016 at 18:08
  • 1
    @Coder256: I don't think your edit was right. "$@" will expand to multiple words, one for each parameter.
    – jwodder
    Mar 16, 2016 at 21:37
16

blade19899's answer is indeed the way to go, however one could also call sudo bash in the shebang:

#!/usr/bin/sudo bash
# ...

The obvious caveat is this will work only as long as the script is called with ./script and will fail as soon as the script is called with bash script.

4
  • 6
    This is one of the reasons why you should never type bash script on the command line to invoke a script. If the script specifies anything other than bash in the #! line, then invoking it with bash script is going to fail. If I tried to invoke a python script using bash script.py it would also fail.
    – kasperd
    Mar 16, 2016 at 12:48
  • 1
    You can run it with perl script, though. Perl, in an effort to be really really nice, checks the shebang line, and if it's not invoking perl it runs the correct program.
    – tbodt
    Mar 16, 2016 at 22:23
  • 1
    This is bad because it is running every command in the script with sudo. It is better to isolate the few commands that really need sudo and add them where necessary. Don't include the evaluation of functions in these sudo calls; they should be as clear and minimal as possible. Mar 17, 2016 at 11:23
  • @DouglasHeld While I may agree with that, this is what the question is asking for: "How can I "embed" request to pkexec or sudo to run the whole script with root privilege?". Pretty sure there would be cases of use in which being able to do this would be useful.
    – kos
    Mar 17, 2016 at 11:33
15

If you'd like a pretty dialog, try something like this. I ripped this straight out of something else I wrote, so it's got extra stuff you might not need or want, but it shows the general idea:

brand="My Software"

# Check that the script is running as root. If not, then prompt for the sudo
# password and re-execute this script with sudo.
if [ "$(id -nu)" != "root" ]; then
    sudo -k
    pass=$(whiptail --backtitle "$brand Installer" --title "Authentication required" --passwordbox "Installing $brand requires administrative privilege. Please authenticate to begin the installation.\n\n[sudo] Password for user $USER:" 12 50 3>&2 2>&1 1>&3-)
    exec sudo -S -p '' "$0" "$@" <<< "$pass"
    exit 1
fi

sudo dialog

This uses whiptail, which you can install if you don't already have it:

sudo apt-get install whiptail
11
  • 1
    Why are you saving the password in a variable?
    – heemayl
    Mar 16, 2016 at 3:52
  • 11
    Don't call exec echo [PASSWORD]! It'll spawn a process with the password on the command-line that any user can see. Either use the built-in command echo (force the built-in version with builtin echo) or the bashism <<< (like so: sudo ... <<< "$pass"); Sh and other shells have Here Documents for these cases. Also, quote the password in case it contains special characters. Mar 16, 2016 at 3:58
  • Alternatively, put the whiptail command into a separate script and use something like SUDO_ASKPASS=/path/to/askpass-with-whiptail.sh sudo -A -p "My password prompt" -- "$0" "$@" to have sudo deal with the custom password prompt. Mar 16, 2016 at 4:12
  • @DavidFoerster That could work, but then you either need two scripts, or you write your askpass script to a temporary file and then pass it to sudo. Mar 16, 2016 at 4:15
  • Ok however others already noted it is undesirable to pass password to a variable. The way id approach this is to pass output of dialog to named pipe as i have shown here askubuntu.com/a/704643/295286 Mar 16, 2016 at 6:50
8

It appears that nobody else has addressed the obvious concern here. Putting sudo within your script that you then distribute promotes bad user habits. (I'm assuming you're distributing it because you mention "from a user point of view.")

The truth is that there is a guideline in using applications and scripts which is similar to the security principle in banking of: Never give out your personal information to someone who calls you and says they're calling "from your bank", and which exists for similar reasons.

The rule for applications is:

Never type in your password when prompted unless you are certain what is being done with it. This applies triply to anyone with sudo access.

If you're typing your password in because you ran sudo on the command line, great. If you're typing it in because you ran an SSH command, fine. If you're typing it in when you log in to your computer, great, of course.

If you just run a foreign script or executable and tamely enter your password when prompted for it, you have no idea what the script is doing with it. It could be storing it in a temp file in plaintext, for all you know, and might even fail to clean up after itself.

Obviously there are separate and additional concerns about running an unknown set of commands as root, but what I'm talking about here is maintaining security on the password itself. Even assuming the application/script is not malicious, you still want your password to be handled securely to prevent other applications from getting hold of it and using it maliciously.

So, my own personal response to this is, the best thing to put in your script if it needs root privileges, is:

#!/bin/bash
[ "$UID" -eq 0 ] || { echo "This script must be run as root."; exit 1;}

# do privileged stuff, etc.
2
  • While I completely agree with you, a user who runs something without knowing what it does as sudo script_name is just as vulnerable, it's the same. Maybe this idea does promote bad habits - I won't say anything about that. But the key is knowing what a program does, and that's user's responsibility. That's the whole idea behind open-source software. As for my own script, well . . . a script is plain text - users can read it if they wanna know what it does Mar 21, 2016 at 9:34
  • 1
    @SergiyKolodyazhnyy it’s similar, but if you type your password directly into the script it’s actually worse. A script run with sudo still doesn’t know your password.
    – Wildcard
    May 25, 2019 at 21:57
7

I preface the commands within the script which need root access with sudo - if the user has not already gained permissions, the script prompts for a password at that point.

example

#!/bin/sh 
mem=$(free  | awk '/Mem:/ {print $4}')
swap=$(free | awk '/Swap:/ {print $3}')

if [ $mem -lt $swap ]; then
    echo "ERROR: not enough RAM to write swap back, nothing done" >&2
    exit 1
fi

sudo swapoff -a && 
sudo swapon -a

This script can either be run as sudo <scriptname> or as <scriptname>. In either case it will ask for the password, only once.

3
  • 3
    The issue here is that there may be multiple commands that need root access, which means calling sudo for 25 different commands redundant - it's easier to call sudo once. Here however, the reason your script calls sudo only once is because sudo has time out of 15 minutes - commands that take longer than that will need to be reprompted. Your way works just . . . not the way I need it to work. Mar 15, 2016 at 16:41
  • @Serg For a small script, the way I had was quick and easy - I'm truly not an elegant programmer! Mar 16, 2016 at 19:28
  • You are the best programmer posting on this page. You are using sudo exactly as it should be used - only where needed and with very clear results expected. Mar 17, 2016 at 11:26
1

I did it this way:

echo -n "Enter password for sudo rights: "
read -s pass

echo $pass | sudo -S [your command here]
2
  • 4
    At least quote your variables.
    – muru
    Mar 19, 2016 at 9:38
  • And quoting your variables might not even help if your password starts with a hyphen.
    – Wildcard
    Mar 21, 2016 at 0:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .