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
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
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.