Everything In Between

If your project so much as pretends to have a profit motive, I will tell you to go fuck yourself and your project.

How to work around “sorry, you must have a tty to run sudo” without sacrificing security

18 comments

While working on $client‘s Linux server last week, I found myself installing a cron job that ran as root. The cron job called a custom bash script that, in turn, called out to various custom maintenance tasks client had already written. One task in particular had to run as a different user.

During testing, I discovered that the odd-ball task failed to run, and found the following error in the system log:

sudo: sorry, you must have a tty to run sudo

I traced this error to a line trying to invoke a perl command as a user called dynamic:

sudo -u dynamic /usr/bin/perl run-periodic-tasks --load 5 --randomly

A simple Google search turned up an obvious solution to the error: use visudo to disable sudo’s tty requirement, allowing sudo to be invoked from any shell lacking a tty (including cron). This would have solved my problem, but it just felt wrong, dirty, and most troublingly insecure.

One reason why sudo ships with the requiretty option enabled by default is, among other reasons, to prevent remote users from exposing the root password over SSH. Disabling this security precaution for a simple maintenance task already running as root seemed totally unnecessary, not to mention irresponsible. Moreover, client‘s script didn’t even need a tty.

Thankfully, there’s a better way: use su --session-command and send the whole job to the background.

su --session-command="/usr/bin/perl run-periodic-tasks --load 5 --randomly" dynamic &

This line launches a new, non-login shell (typically bash) as the other user in a separate, background process and runs the command you passed using the shell’s -c option. Sending the command to the background (using &) continues execution of the rest of the cron job.

A process listing would look like this:

root     28109     1  0 17:10 ?        00:00:00 su --session-command=/usr/bin/perl run-periodic-tasks --load 5 --randomly dynamic
dynamic  28110 28109  0 17:10 ?        00:00:00 bash -c /usr/bin/perl run-periodic-tasks --load 5 --randomly

Note the parent process (PID 28109) is owned by root but the actual perl process (PID 28110) is being run as dynamic.

This in-script solution that replaces sudo -u user cmd with su --session-command=cmd user seems much better than relying on a change in sudo‘s default (and more secure) configuration to me.

Written by Meitar

March 17th, 2010 at 8:21 pm

18 Responses to 'How to work around “sorry, you must have a tty to run sudo” without sacrificing security'

Subscribe to comments with RSS or TrackBack to 'How to work around “sorry, you must have a tty to run sudo” without sacrificing security'.

  1. Thanks for this. I also did not like even allowing root to bypass the requiretty setting in the sudoers file. This works great.

    T Hanson

    8 Sep 10 at 9:18 AM

  2. This is one gem of a one-line code that can save someone A LOT OF aggravation! This coming from a guy who spent last 4 hours trying to figure out how to execute a shell script from within cPanel’s chkservd service. Situations where you need to run sudo but don’t have a tty are not limited to cron jobs. If you are running a site on cPanel, you might want to use its tailwatchd service (which chkservd is a part of) to make sure essential services are always running. Sure, there are restart scripts for all major services but if you are adding one of our own, you’ll have to write a script that is able to restart the service and, of course, for safety you are most likely not going to want to run them as root.

    Oh, and there is another rub: cPanel actually does not allow changing “requiretty” setting: you can change it, but on the next major cPanel upgrade they’ll automatically rewrite it back to a safer default. This was driving me absolutely NUTS!

    I also wanted to make a small suggestion: older versions of su don’t have –session-command= option. It is much safer to use just –command= because it does the same thing and will work on all versions.

    Cheers and keep up the good work!
    Scriptster.

    Scriptster

    16 Sep 10 at 5:56 PM

  3. A small addition to avoid the “This account is currently not available” error when trying to run a command as a user who doesn’t have a valid login shell and

    Add the –shell option

    su –shell=/bin/bash –session-command=”/path/to/command -argument=something” username &

    Kitty

    17 Feb 11 at 7:38 PM

  4. Hi,

    Good suggestion. But I still don’t understand one thing.

    su –session-command= will still prompt for the root password, which means I have to enter the password manually i.e. I can’t run it non-interactively. How do I get around this, because I need to have everything run in auto mode without any user interaction.

    Thanks

    raj

    15 Aug 11 at 7:57 AM

  5. P.S. With sudo, I could automate it by using “echo $password | sudo -S “. But that doesn’t seem possible with su –session-command, right?

    raj

    15 Aug 11 at 8:01 AM

  6. If you are attempting to run a command remotely (assuming /etc/sudoers allows you to run it with NOPASSWD), pass the -t option to ssh:

    ssh -t myhost “sudo -n yum -y update”

    The -n option to sudo will prevent sudo from prompting for a password, if the command requires a password you’ll get an error.

    Mike

    19 Mar 12 at 8:33 AM

  7. [...] to start at boot time. My usual sudo trick doesn’t work in /etc/rc.local, but thanks to this blog post I found a way: [...]

  8. Still useful a few years down the road. Fixed my script. Thanks!

    Ed

    7 Jun 12 at 11:33 AM

  9. Stoil

    21 Jun 12 at 4:39 AM

  10. Other work around, disable the requiretty option from the sudoers file.

    chandan

    4 Jul 12 at 3:29 PM

  11. thanks a lot Mike

    linuxlover

    3 Oct 12 at 2:43 AM

  12. Thanks a lot Mike. The -t suited my situation perfectly

    John

    23 Oct 12 at 3:02 AM

  13. Maybe disabling requiretty in sudoers file in a per-user basis…

    Defaults:username !requiretty

    I’ve applied it for running an specific command as root by a normal user with NOPASSWORD.

    Source:
    http://serverfault.com/questions/111064/sudoers-how-to-disable-requiretty-per-user

    Arle

    4 Apr 13 at 7:55 AM

  14. Sounds good if you’re starting as root. I’m working the opposite direction — srv1 is running a periodic task that wants to grab a backup from certain areas on srv2. I don’t want the task it triggers on srv2 to run as root; but it needs root to grab a few user directory backups.

    The remote task is authenticated via an unsigned private key, so running it as root really isn’t much of an option — I don’t want an unsigned private key for root running around!

    Oh, and I carefully turn *off* pty because I need an 8-bit clean channel (the remote task sends the backup bzipped tar file out stdout back to the periodic task on srv1, which then stores it on srv1).

    So far turning off requiretty for the backup user is looking like my best option.

  15. This worked for me. Thanks!

    Jeff

    21 Dec 13 at 4:17 PM

  16. […] 惊喜很多,要懂得享受! 还有一篇关于出现这个错误的文章: http://maymay.net/blog/2010/03/17/how-to-work-around-sorry-you-must-have-a-tty-to-run-sudo-without-s… […]

Leave a Reply

To skip the moderation queue, enter the BitCoin address from which you will send BTC