How to elevate privileges to root in bash and return back?
Solution 1
My answer. Yet a solution of how to just elevate and decrease privileges (actually, conceptually without even wrapping, existing separately) or to fully switch to root and back is required.
Basing on suggestions of me, LeonidMew and WinEunuuchs2Unix, here is the workaround. The -H
option is usually a good choice so I include it.
There are two workarounds (bash
can be replaced with sh
):
1) sudo -H /bin/bash -c ‘commands’
and
2) sudo -H /bin/bash -c “commands”
(as well as the heredoc format, sudo -H /bin/bash <<EOF <lines with commands> EOF
).
1) Variables from outside are not visible inside automatically. They would have to be moved inside, be double-defined or be passed as arguments. I could not make or pass a short (re)declaration of an external function like $(declare -f functionname)
inside (maybe it is yet possible), but it worked if I just moved it inside.
2) Only copies of variables from outside are passed inside. You will have to escape with \
constructs $(...)
, $PWD
or other locally defined variables, AND commenting with #
may not work (as with the #$(declare -f ...)
). Arguments like $1
are the ones of the whole script and cannot be passed inside as local variables. Externally defined functions are not visible inside but can be redeclared inside like $(declare -f functionname)
.
In the both cases you can get output through files or a fast string-output from stdout (or several space-separated variables) through the wrapping res=$(...)
. You can see an example here: https://stackoverflow.com/a/23567255 . Though after the wrapping all EOLs are converted into spaces. Maybe export
, designed to pass variables to subprocesses, will help to avoid that wrapping or the usage of files, but somehow it didn’t work for me.
The case 1) seems to be the best default choice and will most likely require less modifications of the existing code, contrary to the case 2) when a careful modification of the existing code will usually be required. Maybe you will find the both cases useful simultaneously.
This is my simple example:
tmpdir=/tmp/
tmpfile=/tmp/tmpfile
res=$(sudo -H bash -c 'tmpdir=$1; tmpfile=$2
echo "$tmpdir;"
cd $ tmpdir
echo $(pwd)
echo "A string from file" > $tmpfile
' - $tmpdir $tmpfile)
echo $res
arr=($res)
echo ${arr[0]} # Hellooo
echo ${arr[1]} # world;
echo ${arr[*]} # whole array
echo ${#arr[*]} # number of items
n=0
echo ${#arr[$n]} # length of the n-th element
cat $tmpfile
sudo sh -c 'rm -f $2' - - $tmpfile
Solution 2
To elevate privileges for few commands in script use sudo with heredoc syntax:
possiblevariable=something
sudo /bin/bash <<EOF
cd /somedir
pwd
commandasroot1 "$possiblevariable"
commandasroot2
EOF
nonrootcommand (and not in /somedir)
Testing cd: (working dir changed inside heredoc, but restores as it be before at end of heredoc)
leonid@DevSSD:~$ sudo bash <<EOF
> cd /tmp
> pwd
> EOF
[sudo] password for leonid:
/tmp
leonid@DevSSD:~$
One more example, shows how variables substitution work in heredoc:
leonid@DevSSD:~$ sudo bash <<EOF
cd /tmp
echo $PWD; echo \$PWD
EOF
[sudo] password for leonid:
/home/leonid
/tmp
leonid@DevSSD:~$
Update: example how you can get output into variable
leonid@DevSSD:~$ variable=$(sudo bash <<EOF
cd /tmp
echo $PWD; echo \$PWD
EOF
)
[sudo] password for leonid:
leonid@DevSSD:~$ echo $variable
/home/leonid /tmp
Solution 3
I don't have many scripts that elevate from user privileges to sudo
(super user) powers. Ironically due to the fact your question is about gedit
one of the scripts I have is called sgedit
. It was created because gksu gedit
is no longer supported and because root user can't set tab settings, font preferences, etc.
#!/bin/bash
# NAME: sgedit
# PATH: /mnt/e/bin
# DESC: Run gedit as sudo using $USER preferences
# DATE: June 17, 2018.
# Must not prefix with sudo when calling script
if [[ $(id -u) == 0 ]]; then
zenity --error --text "You cannot call this script using sudo. Aborting."
exit 99
fi
# Get user preferences before elevating to sudo
gsettings list-recursively | grep -i gedit | grep -v history | \
grep -v docinfo | \
grep -v virtual-root | grep -v state.window > /tmp/gedit.gsettings
sudoFunc () {
# Must be running as sudo
if [[ $(id -u) != 0 ]]; then
zenity --error --text "Sudo password authentication failed. Aborting."
exit 99
fi
# Get sudo's gedit preferences
gsettings list-recursively | grep -i gedit | grep -v history | \
grep -v docinfo | \
grep -v virtual-root | grep -v state.window > /tmp/gedit.gsettings.root
diff /tmp/gedit.gsettings.root /tmp/gedit.gsettings | grep '>' > /tmp/gedit.gsettings.diff
sed -i 's/>/gsettings set/g; s/uint32 //g' /tmp/gedit.gsettings.diff
chmod +x /tmp/gedit.gsettings.diff
bash -x /tmp/gedit.gsettings.diff # Display override setting to terminal
# nohup gedit $@ &>/dev/null &
nohup gedit -g 1300x840+1+1220 $@ &>/dev/null &
# Set the X geometry window size (WIDTHxHEIGHT+X+Y).
}
FUNC=$(declare -f sudoFunc)
sudo -H bash -c "$FUNC; sudoFunc $*;"
exit 0
The script must be called in regular user mode. It copies gsettings
for gedit
from user profile to /tmp
. Important settings like font size, line wrap, tab settings, convert tabs to spaces and plug-ins are copied.
Then sudo
password is requested and status is elevated to root.
sudo -H
is used for equivalent to gksu
protection to prevent root
powers from hammering user configuration files.
Next root configuration settings for gedit
are inherited from calling user's profile that was copied into /tmp
.
gedit
is loaded as a background task and user is presented with sudo
version of file opened. For example /etc/default/grub
.
The sudo
powers are immediately dropped and the command line prompt returns. However gedit
is still running in a separate window with file opened for editing.
Related videos on Youtube
J. Denver
Updated on September 18, 2022Comments
-
J. Denver over 1 year
How to elevate privileges to root in
bash
? And then how to return back to exactly the previous state? My question is both about elevating/decreasing privileges and possible troubles with some apps, especially GUI apps (and an “environment”).Say, some apps keep a separate profile for each user and root. GUI & profiles may be not the only problem with an “environment” if I run an app as root, regardless of whether this is their or OS’s bug or a feature.
Say, I need to run a script as root or already run a script as root (like in rc.local), so you might need to decrease the privileges or "completely" switch back to the normal user from there (
su normaluser
does not always work). To run initially a script withsudo -H
and then to switch to another user? This does not always work as needed especially with GUI apps. I believe the trouble is with an "environment", andDISPLAY=:0 ...
orDISPLAY=:0 gtk-launch ...
(gtk-launch
is likely bugged) may be yet not helpful.Say, I have
gedit
with an opened document. If I run as just a normal usergedit /doc2
, it opens with menu visible and in the already existing window in another tab. If I run it as root, it opens up in a separate window and without visible menu. If I run a script throughsudo -H
orsu
and try to rungedit /doc2
there as that normal user (again with sudo (-H) or su), then it works as if I did that as root, not that normal user. I tried alsosudo
options-l, -s, -i
. With other GUI apps that caused far more serious troubles. Some GUI apps have different GUI or don’t run at all as a result. Sometimesrunuser
was helpful for me but not always. And heredoc format (sudo -H /bin/bash <<EOF <lines with commands> EOF
) does not work as expected.It is how many troubles this makes. For more than 1 year I could not find a good universal solution. So is there anything for elevation of privileges and returning back? Or other good workarounds?
And a full example just in case (run as
sudo ./script.sh
or with-H
):cd /somedir some_commands # using the current directory, root privileges and setting some variables, and writing to somefile (better as normal user) sudo -u normaluser /bin/bash -c 'gedit --encoding someencoding /somefile'`
If I run the following bash script as just
./script.sh
gedit --encoding someencoding /somefile
then
gedit
works as it should.Just in case: it is about Ubunu 16.04 xenial, bash version 4.3.48.
Update:
I know I could run commands like
sudo sh -c ‘command1 $somevariable; command2’
or (I've found out it can be several lines)
sudo sh -c ‘command1 $somevariable command2’
or maybe something similar with
bash
. That might not be an option for a large set of commands and does not solve all the problems. And I absolutely don’t need to enter commands interactively. See also my answer.P.S. I think Linux should user-friendly and easy to use.
-
WinEunuuchs2Unix about 5 yearsIt is probably easier to answer if you simply post a small script that doesn't work. Include the error messages displayed by system.
-
J. Denver about 5 yearsI've already done this. It is about gedit. Also I described the situation with "cd" and the scope of variables if I call another script. maybe itself. Ok, I will make separate example.
-
-
J. Denver about 5 yearsMaybe that heredoc would help but yet it is not working. Namely,
cd
does not work there, and I get$PWD
unchanged even with hardcoded path. See my update above. -
LeonidMew about 5 yearscd is working. You get wrong output because $PWD is resolved before sudo executes heredoc. Try
pwd
instead of 'echo $PWD' -
J. Denver about 5 yearsI run
cd /tmp/ echo $(pwd)
and get the path unchanged. -
LeonidMew about 5 yearsUpdated again. In last example you can see how variables should be escaped, to get what you need.
-
J. Denver about 5 yearsThanks! That way it works. But yet that is not quite what I needed. See also my answer. The variant with
sh
requires less modification of the existing code because, e.g.,echo $PWD
orecho $(pwd)
works. Maybe there is an analogical mode withbash
. I like my workaround more. Yet the solution is not found. I thought there is a solution for such a bash stuff. -
LeonidMew about 5 yearsYes, you can pass var by export. Passing output by tmp file considered harmful
-
J. Denver about 5 yearsSomehow I could not in my case. In your case it was not needed and didn't allow me to change a variable from inside. Yes, a tmp file can cause security problem. As well as passing arguments or especially all arguments to not trusted commands.
-
LeonidMew about 5 yearsUpdated with example how you can get output of it
-
J. Denver about 5 yearsOk, it works with heredoc doc. I placed ")" just after EOF...
-
LeonidMew about 5 yearsMake temp file with
tmphost=$(mktemp)
, and trap on exit:trap 'rm "$tmphost"' EXIT
-
J. Denver about 5 yearsI don't like
mktemp
because it generates more lines... And it does not let chose location of the temporary file, say on which drive, in case I will decide to store it permanently. -
J. Denver about 5 yearsThanks! I have improved my answer and have refined my question. After a modification your script could possibly be a workaround for just one app. A good solution would look like to change 1-2 parameters in Ubuntu or better that option
-H
forsudo
would do everything your script does but for all apps. And yet that would be not all. There would be a need to reduce privileges, say, to run fromcrontab
orrc.local
, becausesudo -u normaluser ...
does not always work as needed.