5

Is this safe?

echo "Defaults    insults" >> /etc/sudoers

If yes, can I do this?

echo "## First line" >> /etc/sudoers
echo "### Second line" >> /etc/sudoers
echo "Defaults    insults" >> /etc/sudoers
echo "### Totally the last line" >> /etc/sudoers

Is there a better way to do this incorporating visudo?

I'm making a bash script, this bit needs to turn insults on and off.

13

There are at least 3 ways in which it can be dangerous:

  1. If /etc/sudoers doesn't end in a newline character (which sudo and visudo 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.

  2. echo may fail to write the full string, for instance if the file system is full. For instance, it may just be able to write Defaults in. Which again will break your sudoers file.
  3. 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 the VISUAL environment variable (takes precedence over EDITOR), but only if the env_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
8

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:

umask 0177
echo 'Defaults insults' > /tmp/insults && 

  sudo sh -c 'cp /tmp/insults /etc/sudoers.d; visudo -cf /etc/sudoers.d/insults

|| rm /etc/sudoers.d/insults'

Then removing the setting is as simple as:

sudo rm /etc/sudoers.d/insults

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.

  • >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. I'm sorry, what do you mean by this? Also, I didn't write sudo because I'm root at the moment. – Amin Shah Gilani Jan 16 '15 at 10:17
  • @amingilani Look at the second-last line of my sudoers: #includedir /etc/sudoers.d - this allows me to add entries to sudoers by adding new files to /etc/sudoers.d. If EL5's sudo is not very old, it should support this, and you can create/delete files in /etc/sudoers.d to add/remove sudo configuration changes: visudo -f /etc/sudoers.d/some-file-name. – muru Jan 16 '15 at 10:40
  • @muru writing a new /etc/sudoers.d/insults is the right answer to the OP's problem, althought not the answer to the question asked. If you would write it down as answer I would upvote it – Carlos Campderrós Jan 16 '15 at 16:06
  • @CarlosCampderrós I wasn't able to determine if RHEL5 supported it, but now I think it would, so I have added it. – muru Jan 16 '15 at 18:20
  • Talking of unsafe things, you can't get much worse than using a file with a fixed name in a world writable directory. (what if someone created a symlink /tmp/insults -> /etc/shadow there). – Stéphane Chazelas Jan 16 '15 at 23:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.