How to demand root privileges in a shell script? [duplicate] - linux

This question already has answers here:
How to check if running as root in a bash script
(21 answers)
Closed 7 years ago.
Say you have a shell script that could potentially require root privileges. You don't want to force the user to run it as sudo. However, if it does require that privilege, you want to prompt the user to enter their password, rather than complaining and forcing them to re-enter the command with sudo.
How would you go about doing this in a Bash script? sudo true seems to work, but it feels like a hack. Is there a better way?

Here's what I often do. This is loose pseudo-code but should give you the idea:
# myscript -- possibly execute as sudo
if (passed -e option) then
read variables from envfile
else
...
need_root = ...
# set variables ...
if ($need_root && $uid != 0) then
env [or whatever] > /tmp/envfile
exec sudo myscript -e/tmp/envfile ...
fi
fi
# stuff to execute as root [or not] ...

The command
sudo -nv
checks whether the user has current sudo credentials (-v), but will fail rather than prompting if access has expired (-n).
So this:
if sudo -nv 2>/dev/null && sudo -v ; then
sudo whoami
else
echo No access
fi
will check whether the user's sudo credentials are current, and prompt for a password only if they're not.
There is a possible race condition: the user's credentials could expire just after the check.
As ghoti points out in a comment, this may not work if the sudoers file is set up to allow only certain commands to be executed. For that and other reasons, be sure to check whether each sudo command succeeded or failed.

If your plan is to use sudo for privilege escalation, one wrinkle you may have to deal with is that that sudo can be set up to permit root access to some commands and not others. For example let's imagine you've got a server that runs VirtualBox, with different people managing the applications than are managing the OS. Your sudoers file might contain something like the following:
Cmnd_Alias SAFE = /bin/true, /bin/false, /usr/bin/id, /usr/bin/who*
Cmnd_Alias SHUTDOWN = /sbin/shutdown, /sbin/halt, /sbin/reboot
Cmnd_Alias SU = /bin/su, /usr/bin/vi*, /usr/sbin/visudo
Cmnd_Alias SHELLS = /bin/sh, /bin/bash, /bin/csh, /bin/tcsh
Cmnd_Alias VBOX = /usr/bin/VBoxManage
%wheel ALL=(ALL) ALL, !SU, !SHELLS, !SHUTDOWN
%staff ALL=(ALL) !SU, !SHELLS, NOPASSWD: SAFE
%operator ALL=(ALL) SAFE, SHUTDOWN
%vboxusers ALL=(ALL) NOPASSWD: VBOX
In this case, a member of the vboxusers unix group will always get a successful response to a sudo -nv, because of the existence of the NOPASSWD entry for that group. But a member of that group running any other command than VBoxManage will get a password challenge and a security warning.
So you need to determine whether the command you need to run can be run without a password prompt. If you don't know how sudo is configured on the system where your script is running, the canonical test is to run the command. Running sudo -nv will only tell you whether you are authenticated; it won't tell you what commands you have access to.
That said, if you can safely rely on a sudo configuration where, say, membership in wheel group gives you access to all commands, for example with:
%wheel ALL=(ALL) ALL
then you can use sudo -nv to test for escalation capabilities. But your script might have some things that it runs as root, and some things it doesn't. You might also want to consider other tools besides sudo for privilege escalation.
One strategy might be to set a variable to preface commands if the need is there, but leave the variable blank if you're already root (i.e. running the entire script inside sudo).
if ! which sudo >/dev/null 2>/dev/null; then
PM_SU_CMD="su - root -c"
elif sudo -nv 2>/dev/null; then
PM_SU_CMD="sudo"
else
echo "ERROR: I can't get root." >&2
exit 1
fi
Of course, if we are already root, unset this to avoid potential conflict:
[ `ps -o uid= $$` -eq 0 ] && unset PM_SU_CMD
(Note that this is a query of the system's process table; we don't want to rely on the shell's environment, because that can be spoofed.)
Then, certain system utilities might be made more easily available using functions:
# Superuser versions for commands that need root privileges
find_s () { $PM_SU_CMD "/usr/bin/find $*"; }
mkdir_s () { $PM_SU_CMD "/bin/mkdir -p $1"; }
rm_s () { $PM_SU_CMD "/bin/rm $*"; }
Then within your script, you'd simply use the _s version of things that need to be run with root privileges.
This is of course by no means the only way to solve this problem.

Related

The difference between "sudo sh -c" and "su -c"

I want to start a program as root when booting, and will add one of the following lines to rc.local:
sudo /path/my_prog
sudo sh -c "/path/my_prog"
su -c "/path/my_prog"
What is the difference between this three lines, and which is the correct one?
You don't need any of them; rc.local runs with root privileges. The correct answer is to simply run your command.
/path/my_prog
su makes sense if you are root and want to switch to a different account.
sudo makes sense if you are running on an unprivileged account, and have been granted the rights to switch to another account (often, but not always, root), usually with the requirement to be able to interactively supply your password (though this can be turned off if you really have to; obviously, you need to understand what you are doing before you mess with security-related stuff).
sh -c "command" is just an inefficient way to run command. Running a shell makes sense when you actually require shell features such as wildcard expansion, redirection, etc, or shell builtins like cd.
Ideally, you might want to make sure that my_prog runs on a dedicated unprivileged system account; then, the syntax would be
su otheraccount -c /path/my_prog
where obviously you need to create otheraccount and make sure it has the privileges that are required for performing this particular task.
su optionally lets you add a lone dash before the account name to have it run a login session; but for a service account, this probably does not make sense.
sudo /path/my_prog will execute /path/my_prog with sudo privileges.
sudo sh -c "/path/my_prog" will execute /path/my_prog (specified by the flag -c) with sh using sudo privileges.
su -c "/path/my_prog" will execute /path/my_prog (specified by the flag -c) with your current shell using sudo privileges.
The correct one to use depends on your use case, so actually it's up to you. IMHO, sudo foo and su -c foo are basically the same.

Run bash whiptail script after login with sudo

I'm creating a virtual machine configuration script using whiptail that I'd like to have automatically start after the user logs in. The thing is, that I don't want the user to be root, however because the user will need to be able to change things like hostname, ip address, add directories, etc. they'll need sudo privileges in order for the script to actually do it's job.
What I've done so far is created a user that uses my whiptail shell script:
adduser -M -s /scripts/whiptail_config.sh user1
I've also added user1 to sudoers using visudo:
user1 ALL=(root) NOPASSWD: /scripts/whiptail3_config.sh
And I've changed /etc/init/tty1.conf to automatically log that user in:
exec /sbin/getty -8 38400 tty1 -a user1
Up to this point, my whiptail_config.sh shell script loads fine after bootup and the forced login. However, anything that actually requires the sudo access within the script errors out with the Permission denied... message.
Is what I'm looking to do possible? Are there alternatives that I haven't considered and should? Thanks!
If /scripts/whiptail3_config.sh doesn't do a sudo, then it won't have permissions. You can make the script check the uid under which it's running and sudo to itself.
Something like
#!/bin/bash
[ $UID != 0 ] && exec sudo $0 "$#"
# the rest of the script...
Where do you use sudo? Nowhere.
Run the script directly from inittab.

Use sudo without password INSIDE a script

For some reason I need, as user, to run without sudo a script script.sh which needs root privileges to work.
I saw as the only solution to put sudo INSIDE script.sh. Let's take an example :
script.sh :
#!/bin/sh
sudo apt-get update
Of course, if I execute this script, I get a prompt asking me for a password. Then I added to my sudoers file (at the end to override everything else) :
user ALL=(ALL:ALL) NOPASSWD:/path/to/script.sh
By the way, I also tried the line :
user ALL=(ALL) NOPASSWD:/path/to/script.sh
(I think I didn't fully understand the difference)
But this doesn't solve my problem if I don't use sudo to execute this script :
# ./script.sh
[sudo] password for user:
# sudo ./script.sh
Starts updating...
Well, so I say to myself "Ok, that means that if I have a file refered in sudoers as I did, it will work without prompt only if I call him with sudo, what is not what I want".
So, ok, I create another script script2.sh as following :
script2.sh
#!/bin/sh
sudo /path/to/script.sh
In fact it works. But I am not truly satisfied of this solution, particularly by the fact that I have to use 2 scripts for every command.
This post is then for helping people having this problem and searching for the same solution (I didn't find a good post on it), and perhaps have better solutions coming from you guys.
Feel free to share your ideas !
EDIT 1 :
I want to insist on the fact that this "apt-get update" was just an example FAR from whhat my script actually is. My script has a lot of commands (with some cd to root-access-only config files), and the solution can't be "Well, just do it directly with apt-get".
The principle of an example is to help the understanding, not to be excuse to simplify the answer of the general problem.
From my blog: IDMRockstar.com:
The kicker is that sometimes, I need to run commands as root. Here's the quick and dirty way I accomplish that without divulging the passwords:
#! /bin/bash
read -s -p "Enter Password for sudo: " sudoPW
echo $sudoPW | sudo -S yum update
This way the user is prompted for the password (and hidden from terminal) and then passed into commands as needed, so I'm not running the entire script as root =)
If you have a better, way, I'd love to hear it! I'm not a shell scripting expert by any means.
Cheers!
.: Adam
If you want to run sudo /usr/bin/apt-get update without a password, you need to have the sudoers entry:
user ALL=(ALL:ALL) NOPASSWD:/usr/bin/apt-get update
For the larger issue of the script as a whole, there are two possible approaches:
Approach 1
For each command in the script that needs sudo, create a line in sudoers specifically for that command. In this case, the script can be called normally:
./script1.sh
Approach 2
Place a line in sudoers for the script as a whole. When this is done, the individual commands do not need sudo. However, sudo must be used to start the script as in:
sudo ./script.sh
If your password isn't something you want to be very secure about, (maybe some testing server in the company etc.) you can elevate to sudo in the script via echo like:
echo YourPasswordHere | sudo -S Command
The prompt still prints the "enter password" text to output though. So don't expect it to be neat.
See this Askubuntu post
As you noted, the file that must appear in the sudoers configuration is the one that is launched by sudo, and not the one that runs sudo.
That being said, what we often do, is having something like
user ALL=(ALL:ALL) NOPASSWD:/path/to/script.sh
in the sudo configuration, where script.sh has all the commands that the script has to do.
Then we define either a Bash function or an alias so that script.sh is actually
sudo /path/to/script.sh
The only issue is if some commands must not be run as root, you need to insert some su - user -c "command" commands in the script.
In new /etc/sudoers.d/apt-get file, put single line:
user ALL=(ALL:ALL) NOPASSWD:/usr/bin/apt-get update
Fully qualified path to executable is required here.
Then use following in your script:
sudo apt-get update
Here, fully specified name is not required. Sudo uses PATH environment variable for executable resolution.
While changing and checking sudoers configuration, be sure to keep another root session open for error recovery.
I suggest you look at the sudo environment variables - specifically you can use (and check for) $SUDO_USER. Call your script with sudo (1 entry in sudoers), then do user stuff as SUDO_USER and root stuff as root.
As mentioned by Basilevs you need to add your user to the sudoers file in order to avoid that sudo commands in the script get stuck awaiting the password.
On Ubuntu 16, there is a simpler way: just add the user to the sudo group, like this:
sudo usermod -aG sudo *username*
From then on, it should work like a charm.
Note:
This works only on the condition that the following line is in file /etc/sudoers:
%sudo ALL=NOPASSWD: ALL
(such line gives passwordless sudo privileges at group level, in this case to the sudo group)
(if this line is not present and you want to add it, make sure you use visudo)
Simply, in order to execute commands as root you must use su (even sudo uses su)
As long as you execute sudo ./script2.sh successfully just instead :
sudo su
"#" //commands as root here
"#" exit
//commands as use here
you can make it a shell function with the name sudo, but no other better way i think,however it's the case with scripts inti,rc android ..etc
must be tidy ;)
however this requires you to put NOPASSWD: su wich is totaly secure indeed
any way here just lacks POISX permissions principle which is filtering so dont enable something to all users or vice versa
simply, call sudo as much as you want with no additional thing then:
chown root script.sh
chmod 0755 script.sh
chgrp sudo script.sh
"make root owner of .sh"
"make it read only and exec for others"
"and put it in sudo group"
of course under sudo
that's it

Should bash scripts use sudo, or assume sudo?

Which pattern is preferable:
#!/bin/bash
echo Installing blah
apt-get install -y blah
...which will fail if run without root perms, or:
#!/bin/bash
echo Installing blah
sudo apt-get install -y blah
...which will succeed as long as the user has sudo access.
I have tended to use the second pattern, but it seems to be rare, so I'm asking what its disadvantages are. The benefits I see are:
It's clear which commands actually require superuser permissions to run (useful if the reader wants to pull the script apart)
Saves a few keystrokes for the user.
I guess downsides include that the use of root permissions might be surprising ("I didn't type sudo, so I didn't expect anything like apt-get to be run...."). What else?
I will prefer second Pattern,
because running few commands with sudo is better rather then running entire script with root permissions in which some commands are needless to have root access and so if you perform these commands with root user further using that command outputs will again need root access or you will have to change the ownership.
So I will prefer second approach where sudo is necessary do it, else go local user.
You could always test if the user is root (id -u gets the uid of the current user, and 0 is root), and run sudo if they aren't -
#!/bin/bash
echo Installing blah
CMD=""
if [ "$(id -u)" != "0" ]; then
CMD="sudo"
fi
${CMD} apt-get install -y blah
The more common approach would be to exit with an error condition if they aren't root,
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
echo "sudo $0 (must run as root)"
exit 1
fi
echo Installing blah
apt-get install -y blah
The second option is the correct option. Commands that don't require root access should not be run as root just to simplify your script. Commands that do require root access (or access by any specific user, for that matter) should be run by sudo. For clarity, you can use the -p option to sudo to present a custom prompt which can explain exactly why the user is asked for their password.
Option two is also preferable because sudo is highly configurable, and the user may already have permission to run specific commands with sudo without a password, so it's possible the user would not be inconvenienced by a password prompt. It's less likely that the user is allowed to sudo arbitrary commands (such as your script) without a password.

How to run command in su mode in bash script?

I need to run these two commands :
ulimit -s 1024
echo 120000 > /proc/sys/kernel/threads-max
The first one can be run just in user mode (not using sudo or su) and the second can only be run in su mode. I want to write a bash script that let me run these two commands. The first one is OK. For the second one, I need to su (change user to root), run the command, and then exit. Actually, I want to run the second command in su mode using a bash script. Any idea?
If your user has permission to use "sudo tee", then one solution is:
echo 120000 | sudo tee /proc/sys/kernel/threads-max
As a security measure, you cannot run scripts as a superuser without prepending sudo. If you want it to be passwordless, you need to run visudo and allow your (or the executing user) to run this command as a superuser without password confirmation.
The other way is to use the setuid bit on compiled code. Compile a simple program which will execute the echo 120000 > /proc/..., then change it to be owned by root: chown 0:0 executable_name, and chmod u+s executable_name to set the setuid bit on it. This will cause execution of this program to be ran with permissions of its owner, which is root.
This is the same way which allows passwd to modify a file which requires super-user privileges without actually being a super-user or sudoer.

Resources