Why is everyone using sudo wrong? Or is it me?

By thomas, 28 January, 2014

While configuring OMD (yes, Orchestral Manoeuvers in the Dark, no, not really) I ran into a point at which apache was supposed to run as the OMD user for check_mk. Hard coded into the check_mk configuration is a call to

sudo su - check_mk -c check_mk\ --automation\ *

I've seen this many times, sysadmins doing sudo su -, but why? It reminds me of an admin who would routinely do cat file | less, or my favourite cat file | grep something | wc -l when grep -c something file would be just about as nice. The other problem I have with getting su in the middle is that you could have much cleaner sudoer files. I might be in a minority of opinion though, the man page for sudo has examples with su in them, why, I don't know.

In the above code we are saying, sudo to root, run su as root, then tell su to drop to a specific user and run a command given with -c, we could have just as easily said:

apache ALL=(omduser) NOPASSWD:SETENV: ALL

Then changed the command run to this:

sudo -u omduser check_mk --automation --

This is fundamentally different, We aren't asking to run anything as root, we drop privs much sooner in the game and become the omduser to run the command instead of running su as root and relying on su to drop privs. Not only that, we have just allowed the apache user to run anything as omduser, so if we need to run some other omd command later, it's already covered. We should lock it down true, but I would feel a lot better about a wildcard in that last sudoers, the absolute worst case would be that apache could run an arbitrary command as omduser.

sudo su - I know I've typed it, but it's just wrong, I should be doing

sudo -i

It must be correct, it has less characters in it.

So I started looking and I couldn't find a best practices doc for using sudo. Until I find it, I thought I would start something here from what limited experience I have. I present my version of...

Best practices for sudo

  • Always edit sudoers with visudo
  • If your sudo supports sudoers.d, use it.
  • If UserA needs to run commands as UserB, use the Runas_Spec to allow UserA to run as UserB

    UserA ALL = (UserB) ALL
  • Use wildcards sparingly and only in arguments


    UserA ALL = (ALL) /opt/vendor/version-*/bin/something
    UserA ALL = (ALL) /opt/vendor/version-11.0.1/bin/something *

    The first rule is more dangerous than the second, the command

    sudo /opt/vendor/version-11.0.1/../../../usr/bin/vim

    Matches the wildcard and permits UserA to run vim as root. The second rule allows UserA to run /opt/vendora/version-11.0.1/bin/something with any arbitrary arguments, so provided we trust that the command 'something' doesn't have any shell escapes, we should be ok.
  • Only allow the use of sudoedit for editing files. Most editors provide shell escapes. If you permit a user to run vim for instance, they can execute any command using :!
  • Only allow users to run commands as root in paths in which they do not have write access. If UserA can edit '/opt/vendora/version-11.0.1/bin/something', then UserA cannot be allowed to execute 'something' as root.
  • If any user is allowed root access, then no other user should be allowed open access to that user. This is known as user hopping.

    UserA ALL = (ALL) NOPASSWD: ALL
    UserB ALL = (UserA) NOPASSWD: ALL

    UserB can run any command as UserA, but UserA can run any command as root. So to get a root shell, all UserB has to do is sudo -u userA sudo bash

    UserB@host ~ $ sudo -u UserA sudo bash
    root@host /home/UserB $
  • Never forget that Runas_spec has a group component...

    UserA ALL = (:wheel) NOPASSWD: ALL


    root@host /etc/sudoers.d $ sudo -iu UserA
    UserA@host ~ $ touch hello
    UserA@host ~ $ sudo -g wheel touch there
    UserA@host ~ $ ls -l hello there
    -rw-rw-r--. 1 UserA UserA 0 Jan 28 22:35 hello
    -rw-r--r--. 1 UserA wheel 0 Jan 28 22:35 there
  • Teach your users to use sudo -l

I bet this is written down somewhere famous though...

Other run things I noticed...useradd UserA creates UserA, not usera, it's a distinctly different user...rabbithole approaching...

root@host $ id usera
id: usera: no such user
root@host $ id UserA
uid=1003(UserA) gid=1003(UserA) groups=1003(UserA)