Reputation: 78549
I'm writing a script that requires root level permissions, and I want to make it so that if the script is not run as root, it simply echoes "Please run as root." and exits.
Here's some pseudocode for what I'm looking for:
if (whoami != root)
then echo "Please run as root"
else (do stuff)
fi
exit
How could I best (cleanly and securely) accomplish this? Thanks!
Ah, just to clarify: the (do stuff) part would involve running commands that in-and-of themselves require root. So running it as a normal user would just come up with an error. This is just meant to cleanly run a script that requires root commands, without using sudo inside the script, I'm just looking for some syntactic sugar.
Upvotes: 474
Views: 404991
Reputation: 2909
Read official GNU Linux documentation, there are many ways to do it correctly.
Make sure you put the shell signature to avoid errors in interpretation:
#!/bin/bash
This is my script:
#!/bin/bash
if [ $EUID -gt 0 ]; then
echo "Please run as root/sudo"
exit 1
else
#do your stuff
fi
Upvotes: 16
Reputation: 23259
Update: I have packaged this into an is_root script of my BashScripts framework. When run as a standalone command, it will echo out "Running as root". If called as a bash function, it will return true
if root.
Here is the definitive answer to this question.
If using Bash, and you don't care about incompatible shells, the definitive best solution is:
SUDO=""
if (( $EUID != 0 )); then
echo "Not running as root. Running via sudo..."
SUDO="sudo"
fi
$SUDO ls /root
If you care about maximum portability, independent of shell, run this:
if [ -z "$EUID" ]; then
EUID=$(id -u)
fi
if [ "$EUID" -ne 0 ]; then
echo "Error: Not running as root/superuser."
exit;
fi
I have just tested this on NetBSD and msys+bash on Windows, and on dash. It will work anywhere that has the id
command or bash.
Upvotes: 0
Reputation: 8996
It is important to notice that whenever you run a script using sudo
the 'user context' or environment will switch to root
.
Well, my young padawan, this means that if a padawan
user runs a script that contains a tilde (~
) using sudo
, whenever the bash will expand ~
the result will be /root
and not /home/<user>
(i.e., in this case /home/padawan
), or if you create either a directory or a file the owner and group will be root
and not the that executed the script in this case padawan
, because the user environment was switched.
For instance, lets check this script install-app.sh
:
#!/bin/bash
ROOT_UID=0 # Only users with $UID 0 have root privileges.
E_NOTROOT=87 # Non-root exit error.
## Prevent the execution of the script if the user has no root privileges
if [ "${UID:-$(id -u)}" -ne "$ROOT_UID" ]; then
echo 'Error: root privileges are needed to run this script'
exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
service mysql start
...
If we run using sudo
:
sudo install-app.sh
This will create directories and a config
file will look like this:
##
## ~ (/root)
drwxr-xr-x 17 root root 4096 Nov 23 20:45 ./
drwxr-xr-x 5 root root 4096 Nov 15 19:04 ../
...
drwxr-xr-x 3 root root 4096 Nov 25 14:30 app/
...
drwxr-xr-x 2 root root 4096 Nov 16 19:08 tmp/
## ~/app (/root/app)
drwxr-xr-x 3 root root 4096 Nov 25 14:30 ./
drwxr-xr-x 17 root root 4096 Nov 25 14:33 ../
drwxr-xr-x 2 root root 4096 Nov 25 14:33 init/
## ~/app/init (/root/app/init)
drwxr-xr-x 2 root root 4096 Nov 25 14:33 ./
drwxr-xr-x 3 root root 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root root 0 Nov 25 14:33 config
## /home/<user>/app/conf
drwxr-xr-x 2 <user> <user> 4096 Nov 25 14:43 ./
drwxr-xr-x 3 <user> <user> 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root root 0 Nov 25 14:43 profile
As you can see the script is a total mess. Now the <user>
cannot get access to the profile
file, nor can they modify the config
without using sudo
. At the beginning, this may seem not important, but I assure you that if your project gets bigger, someone will run the script and mess with your system.
Prompt the user to verify if they are a sudoer. Then, add sudo
to the commands that require it.
The following code shows how to apply these changes to the script:
#!/bin/bash
E_NOTROOT=87 # Non-root exit error.
## Prevent the execution of the script if the user has no root privileges
## Check if is sudoer
if ! $(sudo -l &>/dev/null); then
echo 'Error: root privileges are needed to run this script'
exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
sudo service mysql start
...
This modification allows the user to run the script like this:
install-app.sh
The user will be requested to insert his password to verify if is sudoer. After,mkdir -vp ~/app/init
will create the file in the user's home:
/home/<user>/app/init
/home/<user>/app/init/config
/home/<user>/app/init/profile
Also, I recommend to get the users homer directory and use it as a constant.
## Defines user home directory
USER_HOME_DIR=$(getent passwd ${SUDO_USER:-$USER} | cut -d: -f6)
...
mkdir -vp "$USER_HOME_DIR/app/init"
...
Upvotes: 4
Reputation: 78549
A few answers have been given, but it appears that the best method is to use is:
id -u
If run as root, this command will return an id of 0.
This appears to be more reliable than the other methods, and it seems that it return an id of 0 even if the script is run through sudo
.
Here is an example of checking if running as root in a bash script (using `id -u` inline to do so):
#/bin/bash
if [ $(id -u) -ne 0 ]
then echo Please run this script as root or using sudo!
exit
fi
# Remainder of script goes below this line
Upvotes: 217
Reputation: 11966
I just had that problem, but I solved it in a way that could also work for another user than root. The issue being that you know that root has an ID of 0, but you won't want to use another ID, as it is arbitrary and not guaranteed to be the same if you change your server or stuff.
if [ "$(whoami)" == root] # you could put another user name here instead of root
then
# Do stuff
else
# Do another kind of stuff
fi
Upvotes: 0
Reputation: 4028
I don't really think UID-based answers are any good here.
There can be privileged users, that where UID != 0
, there can also be systems where uid
or other commands are not available.
Most of the time the best solution seems to me to just try the command without root, and then do it again with root privileges or run a command that 100% requires root and to something if it fails with a permissions error.
Upvotes: 0
Reputation: 6036
#!/usr/bin/env sh
[ "`id -u`" -ne 0 ] && echo "Please run me as root!"
Unlike all other answers, this script:
Does not depend on anything being named "root", such as the user name or a /root
directory.
Is compatible with and tested on:
$EUID
is specific to bash and zsh)$()
)Only "fish" is a shell that is not compatible (there seems to be no way to have compatibility between the more commons shells above and fish).
Works on:
I'd be interested if anyone has a macOS or Windows Linux Subsystem machine to test this on as well! Please comment or edit this answer.
Detects:
sudo
(tested version: 1.9.5p2)doas
(tested version: 6.8.1-2)su
+ password)Fakeroot is not detected, but if the user wants to pretend they're root, I see no reason to not let them.
Upvotes: 4
Reputation: 80398
My one-liner:
[ "$(whoami)" != root ] && echo "Please, run as root." >&2 && exit 1
Tested under Debian, Ubuntu and Docker.
Upvotes: 0
Reputation: 3486
In this answer, let it be clear, I presume the reader is able to understand the difference betweeen modern shells like bash
, zsh
and others vs portable POSIX shells like dash
.
I believe there is not much to explain here since the highly voted answers do a good job of explaining much of it.
Yet, if there is anything to explain further, don't hesitate to comment, I will do my best by filling the gaps.
New solution:
# bool function to test if the user is root or not
is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }
is_user_root__benchmark
)#+------------------------------------------------------------------------------+
#| is_user_root() benchmark |
#| "Bash is fast while Dash is slow in this" |
#| Language: POSIX shell script |
#| Copyright: 2020-2021 Vlastimil Burian |
#| M@il: info[..]vlastimilburian[..]cz |
#| License: GPL 3.0 |
#| Version: 1.2 |
#+------------------------------------------------------------------------------+
readonly iterations=10000
# intentionally, the file does not have an executable bit, nor it has a shebang
# to use it, just call the file directly with your shell interpreter like:
# bash is_user_root__benchmark ## should take a fraction of one second
# dash is_user_root__benchmark ## could take around 10 seconds
is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }
print_time () { date +"%T.%2N"; }
print_start () { printf '%s' 'Start : '; print_time; }
print_finish () { printf '%s' 'Finish : '; print_time; }
printf '%s\n' '___is_user_root()___'; print_start
i=1; while [ "$i" -lt "$iterations" ]; do
is_user_root
i=$((i+1))
done; print_finish
Examples of use and duration:
$ dash is_user_root__benchmark
___is_user_root()___
Start : 03:14:04.81
Finish : 03:14:13.29
$ bash is_user_root__benchmark
___is_user_root()___
Start : 03:16:22.90
Finish : 03:16:23.08
Since it is multitude times faster to read the $EUID
standard bash
variable, the effective user ID number, than executing id -u
command to POSIX-ly find the user ID, this solution combines both into a nicely packed function. If, and only if, the $EUID
is for any reason not available, the id -u
command will get executed, ensuring we get the proper return value no matter the circumstances.
Well, if I see correctly, there does seem to be a missing piece of code above.
You see, there are many variables which have to be taken into account, and one of them is combining performance and reliability.
#!/bin/sh
# bool function to test if the user is root or not (POSIX only)
is_user_root ()
{
[ "$(id -u)" -eq 0 ]
}
if is_user_root; then
echo 'You are the almighty root!'
# You can do whatever you need...
else
echo 'You are just an ordinary user.' >&2
exit 1
fi
As much as you possibly don't like it, the Unix / Linux environment has diversified a lot. Meaning there are people who like bash
, zsh
, and other modern shells so much, they don't even think of portability (POSIX). It is nowadays a matter of personal choice and needs.
Upvotes: 50
Reputation: 2071
My one-liner:
if [ "$(id -u)" -ne 0 ]; then echo "Please run as root." >&2; exit 1; fi
Upvotes: 106
Reputation: 5972
Try the following code:
#!/bin/sh
if [ "$(id -u)" -ne 0 ]; then
echo "Sorry, you are not root." >&2
exit 1
fi
Note on edit: Backticks are considered deprecated by now (many further notes).
Upvotes: 5
Reputation: 1567
As far as I know, the correct way to check it, is as follows:
#!/bin/sh
if [ "$(id -u)" -eq 0 ]; then
echo "You are root."
else
echo "You are NOT root." >&2
fi
OP's Note: you may see "Testing For Root" section on linuxcommand.org.
Editor's Note: I have slightly adjusted this shell snippet to be POSIX-compatible.
Upvotes: 3
Reputation: 349
The problem using: id -u
, $EUID
and whoami
, is all of them give false positive when I fake the root, like this:
$ fakeroot
id
:
$ id -u
0
$EUID
:
$ echo $EUID
0
whoami
:
$ whoami
root
Then a reliable (and hacking) way is verify if the user has access to the /root
directory:
$ ls /root >/dev/null 2>&1 && is_root=true || is_root=false; echo "$is_root"
Upvotes: 4
Reputation: 806
id -u
is much better than whoami
, since some systems like Android may not provide the word root.
Example command:
# whoami
Example output:
whoami: unknown uid 0
Upvotes: 3
Reputation: 1
My check for the root
user, running in Bash:
#!/bin/bash
if [ "$UID" -eq 0 ]; then
echo "You are root."
else
echo "You are just an ordinary user." >&2
fi
Upvotes: -2
Reputation: 2085
My two cents, running in Bash:
#!/bin/bash
# Display the UID variable
echo "Your UID is $UID"
# Check for root user via UID
if [ "$UID" -eq 0 ]; then
echo "You are root"
else
echo "You are not root user" >&2
fi
Upvotes: -1
Reputation: 21934
There is a simple check for the user being root
.
# Fancy red-colored `error` function with `stderr` redirection with `exit`.
error ()
{
{ printf '\E[31m'; echo "$@"; printf '\E[0m'; } >&2
exit 1
}
# Test for root user.
if [ "$EUID" -eq 0 ]; then
error "Do not run this as the root user"
fi
This also assumes that you want to exit with a 1 if you fail. The error
function is some flair that sets output text to red (not needed, but pretty classy if you ask me).
Upvotes: 39
Reputation: 167
Very simple way just put:
if [ "$(whoami)" = "root" ]; then
# you are root
else
# you are not root
fi
The benefit of using this instead of id
is that you can check whether a certain non-root user is running the command, too; eg.
if [ "$(whoami)" = "john" ]; then
# you are john
else
# you are not john
fi
Editor's Note: Either prefer using POSIX-compatible test command and comparison [... = ...]
or you can use Bash/+other shells specific [[... == ...]]
.
Upvotes: 11
Reputation: 1116
One simple way to make the script only runnable by root is to start the script with shebang:
#!/bin/su root
Upvotes: 9
Reputation: 47
I stumbled on the same issue. The line #!/bin/su root
would be an elegant method, but does not work on certain (higher) versions of bash, as mentioned by @basickarl. Further - this method will probably fail when you start the script with /bin/bash ./some_script.(b)sh
.
I found an other page on SO where the use of the environment variables SUDO_UID
and SUDO_USER
were used. When a normal user invokes a script with sudo
these are available in the script. Otherwise they are empty. Also: when you are actually logged on as root these values are empty (or unset - not entirely sure) as well.
So this is the solution I'm currently using:
isSudo()
{
local _fn="${FUNCNAME[0]}"
if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
>2 echo -e "$_fn: invoked as sudo-user ($SUDO_USER)."
else
>2 echo -e "$_fn: invoked as root ($SUDO_USER)."
fi
return 0
fi
>2 echo "$_fn: not as root or as sudoer."
return 1
}
Then use it like this eg.:
if $(isSudo) ; then
echo -e "Script started as root or as sudoer."
else
echo -e "Script not started as root or with sudo"
fi
The >2
in front of the echo is to redirect the echo to stderr otherwise the if
statement goes haywire and it's just for a little debugging. In stead of using an echo, you could save "root", "sudoer" or "user" in a local variable and let the function echo that back and then perform a case
on that like this:
how_invoked()
{
local _invoked_as="USER"
if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
_invoked_as="SUDOER"
else
_invoked_as="ROOT"
fi
fi
echo -n "$_invoked_as" # -n prevents newline
}
And then use it like this:
INVOKED_AS=$(how_invoked)
case $INVOKED_AS in
USER)
echo -e "Invoked as user." ;;
SUDOER)
echo -e "Invoked as sudoer." ;;
ROOT)
echo -e "Invoked as root." ;;
*)
echo -e "I'm confused" ;;
esac
Or if you just want to test for one of those:
if [ "$(how_invoked)" == "SUDOER" ] ; then
echo -e "You are a sudoer."
else
echo -e "You are NOT a sudoer."
fi
Hope this helps.
Upvotes: 0
Reputation: 7337
Check if you are root and quit if you are not:
if ((EUID != 0)); then
echo "Root or Sudo Required for script ( $(basename $0) )"
exit
fi
Or in this example, try to create a directory in root location then try after rights were elevated.
Check if you are root and if not elevate if possible :
# Fails to create these dirs (needs sudo)
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)
if ((EUID != 0)); then
echo "Granting root privileges for script ( $(basename $0) )"
if [[ -t 1 ]]; then
sudo "$0" "$@"
else
exec 1> output_file
gksu "$0" "$@"
fi
exit
fi
echo "Root privileges granted..."
# Creates Dirs as it now has rights
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)
Upvotes: 1
Reputation: 10064
The $EUID environment variable holds the current user's UID. Root's UID is 0. Use something like this in your script:
if [ "$EUID" -ne 0 ]
then echo "Please run as root"
exit
fi
Note: If you get 2: [: Illegal number:
check if you have #!/bin/sh
at the top and change it to #!/bin/bash
.
Upvotes: 669
Reputation: 4645
In a bash script, you have several ways to check if the running user is root.
As a warning, do not check if a user is root by using the root
username. Nothing guarantees that the user with ID 0 is called root
. It's a very strong convention that is broadly followed but anybody could rename the superuser another name.
I think the best way when using bash is to use $EUID
, from the man page:
EUID Expands to the effective user ID of the current user, initialized
at shell startup. This variable is readonly.
This is a better way than $UID
which could be changed and not reflect the real user running the script.
if (( $EUID != 0 )); then
echo "Please run as root"
exit
fi
A way I approach that kind of problem is by injecting sudo
in my commands when not run as root. Here is an example:
SUDO=''
if (( $EUID != 0 )); then
SUDO='sudo'
fi
$SUDO a_command
This ways my command is run by root when using the superuser or by sudo
when run by a regular user.
If your script is always to be run by root, simply set the rights accordingly (0500
).
Upvotes: 106
Reputation: 23873
As @wrikken mentioned in his comments, id -u
is a much better check for root.
In addition, with proper use of sudo
, you could have the script check and see if it is running as root. If not, have it recall itself via sudo
and then run with root permissions.
Depending on what the script does, another option may be to set up a sudo
entry for whatever specialized commands the script may need.
Upvotes: 30
Reputation: 362137
If the script really requires root access then its file permissions should reflect that. Having a root script executable by non-root users would be a red flag. I encourage you not to control access with an if
check.
chown root:root script.sh
chmod u=rwx,go=r script.sh
Upvotes: 5