Is 'echo "Defaults insults" >> /etc/sudoers' safe?
There are at least 3 ways in which it can be dangerous:
If
/etc/sudoers
doesn't end in a newline character (whichsudo
andvisudo
allow), for instance, if it ends in a non-terminated#includedir /etc/sudoers.d
line, your command will make it:#includedir /etc/sudoers.dDefaults insults
which will break it and render
sudo
unusable.echo
may fail to write the full string, for instance if the file system is full. For instance, it may just be able to writeDefaults in
. Which again will break yoursudoers
file.- On a machine with multiple admins, if both attempt to modify
/etc/sudoers
at the same time, the data they write may be interlaced.
visudo
avoids these problems because it lets you edit a temporary file instead (/etc/sudoers.tmp
), detects if the file was modified (unfortunately not if the file was successfully modified as it doesn't seem to be checking the editor's exit status), checks the syntax, and does a rename
(an atomic operation) to move the new file in place. So it will either successfully update the file (provided your editor also leaves the file unmodified if it fails to write the new one) or fail if it can't or the syntax is invalid.
visudo
also guards against several persons editing the sudoers
files at the same time.
Now, reliably using visudo
in an automatic fashion is tricky as well. There are several problems with that:
- You can specify an editor command for
visudo
with theVISUAL
environment variable (takes precedence overEDITOR
), but only if theenv_editor
option has not been disabled. - my version of
visudo
at least, under some conditions, edits all of/etc/sudoers
and all the files it includes (runs$VISUAL
for all of them). So you have to make sure your$VISUAL
only modifies/etc/sudoers
. - as seen above, it doesn't check the exit status of the editor. So you need to make sure the file your editor saves is either successfully written or not modified at all.
- It prompts the user in case of problem.
Addressing all those is a bit tricky. Here is how you could do it:
NEW_TEXT='Defaults insults' \
CODE='
if [ "$2" = /etc/sudoers.tmp ]; then
printf >&2 "Editing %s\n" "$2"
umask 077
{
cat /etc/sudoers.tmp && printf "\n%s\n" "$NEW_TEXT"
} > /etc/sudoers.tmp.tmp &&
mv -f /etc/sudoers.tmp.tmp /etc/sudoers.tmp
else
printf >&2 "Skipping %s\n" "$2"
fi' \
VISUAL='sh -fc IFS=:;$1 sh eval:eval:"$CODE"' visudo < /dev/null
Won't work if env_editor
is unset.
On a GNU system, a better alternative would be to use sed -i
which should leave sudoers.tmp
unmodified if it fails to write the newer version:
Add insults
:
SED_CODE='
/^[[:blank:]]*Defaults.*insults/,${
/^[[:blank:]]*Default/s/!*\(insults\)/\1/g
$q
}
$a\Defaults insults' \
CODE='
if [ "$2" = /etc/sudoers.tmp ]; then
printf >&2 "Editing %s\n" "$2"
sed -i -- "$SED_CODE" "$2"
else
printf >&2 "Skipping %s\n" "$2"
fi' \
VISUAL='sh -fc IFS=:;$1 sh eval:eval:"$CODE"' visudo < /dev/null
Remove insults:
SED_CODE='
/^[[:blank:]]*Defaults.*insults/,${
/^[[:blank:]]*Defaults/s/!*\(insults\)/!\1/g
$q
}
$a\Defaults !insults' \
CODE='
if [ "$2" = /etc/sudoers.tmp ]; then
printf >&2 "Editing %s\n" "$2"
sed -i -- "$SED_CODE" "$2"
else
printf >&2 "Skipping %s\n" "$2"
fi' \
VISUAL='sh -fc IFS=:;$1 sh eval:eval:"$CODE"' visudo < /dev/null
I poked around a bit and seems includedir
support is available in sudo
going back a long way (~2001 from what I can tell), so it should be supported in RHEL 5. Look for an include directive in your sudoers:
sudo grep '#includedir' /etc/sudoers
If you get a result, then ideally you should add and remove files in that directory, and it is usually /etc/sudoers.d
. This path may be difficult to determine in a shell script.
At any rate, assuming sudoers.d
, you should add files with permissions at most 0600
in that directory. Something like:
sudo sh -c '
umask 0177;
filename="/etc/sudoers.d/$1";
echo 'Defaults insults' > "$filename";
visudo -cf "$filename" || rm "$filename"
' _ 99-insults
(Where 99-insults
is the name of the file to be created in sudoers.d
.)
Then removing the setting is as simple as:
sudo rm /etc/sudoers.d/99-insults
And indeed, that's what the last part of the sh -c
command does - remove this file if visudo
fails its check, so that you're not left with a broken sudoers
.
You can use tee -a
as the EDITOR
:
$ sudo VISUAL='tee -a' visudo <<<"Defaults insults"
Defaults insults
$ sudo tail /etc/sudoers
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
# See sudoers(5) for more information on "#include" directives:
#includedir /etc/sudoers.d
Defaults insults
I don't know if EL5's sudo
has includedir
support, but if it does, then create new files in it with visudo -f
, please.