3

I am creating an automation script. As part of it, I want to add a cron job. Here's a part of the script that fails:

BACKUP_USER=backupbot
SCRIPT_NAME=backup-script.sh

scp -i ./ssh-key ./$SCRIPT_NAME user@server:/tmp
ssh -i ./ssh-key user@server "
    sudo mv /tmp/$SCRIPT_NAME /home/$BACKUP_USER/bin/ &&
    sudo chown $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME &&
    sudo chmod 100 /home/$BACKUP_USER/bin/$SCRIPT_NAME &&
    sudo sed -i 's/THE_URL/'${1}'/' /home/$BACKUP_USER/bin/$SCRIPT_NAME &&
    sudo echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME > /etc/cron.d/discourse-backup"

The problematic command is:

sudo echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME > /etc/cron.d/discourse-backup

I'm getting:

bash: line 5: /etc/cron.d/discourse-backup: Permission denied

Until this one, everything is executed as it should. What is the issue with my last command? I thought it is some problem with quotes - I tried multiple combinations of single- and double- quotes, but I ended up with the same (or worse) results.

1
  • 3
    The redirection is done by your shell as the normal user before running sudo. – Bodo Apr 13 at 15:10
7

When you run a command like

sudo echo some text > file

the redirection is done by your shell as the normal user before running sudo.

Edit, answering a comment:

The shell doesn't treat sudo as anything specific compared to other commands, and it doesn't know that sudo will run with elevated privileges.

The shell's behavior will be the same as with

/bin/echo some text > file

When the shell parses one of the command lines above, it finds the redirection. So it will first open the file, then fork a process for the program to execute, dup the file descriptor to stdout and exec the program. Then either /bin/echo or sudo is run with stdout already redirected.

In your use case, opening the file for the redirection will fail as the normal user.

Try something like

echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME | sudo tee /etc/cron.d/discourse-backup >/dev/null

In this case, the file is a command line argument for sudo which will run as root and then passed the file name argument to tee which will then be executed with elevated privileges. This will allow tee to open the file for writing.

2nd edit: This answer was focused on solving the problem related to sudo and redirection not on other possible problems. As mentioned by user cas in a comment, the variables should be quoted, either individually or as the entire string, e.g.

echo "*/1 * * * * $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME"  | sudo ...

In the use case of the question, the quoting might be less critical for two reasons. The arguments are used for echo only, and the output must be a valid crontab line. This forbids several "problematic" characters in the variables anyway. But in general, correct quoting is always recommended.

As this command would be part of a longer quoted string, the quotes could be escaped, e.g.

ssh -i ./ssh-key user@server "
    ...
    echo \"*/1 * * * * $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME\" | sudo ... "
9
  • That works. The "redirection as the normal user" behavior surprised me though. – Loreno Apr 13 at 15:29
  • 1
    @Loreno I added an explanation. – Bodo Apr 13 at 16:45
  • I don't understand how this is going to work. sudo is going to ask for a password if it hasn't been used within a few minutes and it won't get one because stdin is /dev/null. – Joshua Apr 13 at 23:33
  • 1
    @Loreno use heredocs. e.g. <<EOF ... EOF or <<<"$var" – cas Apr 14 at 6:21
  • 1
    @Loreno It is difficult to recomment reading materials for 25 years of experience. A good resorce are the questions and answers here on the sites of the stack exchange network. Often the answers contain references to other sources of information. For knowledge about bash I can recommend mywiki.wooledge.org/BashFAQ – Bodo Apr 14 at 12:43
-2

As explained by @Bodo, the problem is the local shell interpreting the meta characters [the '>' in this case] before shipping the string via ssh to the remote host.

An alternative to using the 'tee' command is to simply escape the offending meta character with a reverse-slash '\' [the '>' or any other meta sequence e.g. '>>', '$1', '*', etc...] so that the you have something like:

sudo echo '*/1 * * * *' $BACKUP_USER /home/$BACKUP_USER/bin/$SCRIPT_NAME \> /etc/cron.d/discourse-backup

I'd also like to note that the reason the '*' characters in the echo statement didn't create problems is that they are enclosed in a hard (single) quote pair to inhibit the shell expansion which would have otherwise locally occurred.

Also in this example, note that the environmentals $BACKUP_USER and $SCRIPT are locally defined and not something which is defined on the remote system. If you wanted to use any environmentals defined on the remote system, you'd need to escape the '$' symbol.

A short rule of thumb: If the transmitted string is not quoted or within soft (double) quotes, a meta sequence may need to be escaped. If it's in hard (single) quotes escaping is usually not required

This all assumes you have permissions to write to the destination file. If you don't, alternatives are using the 'tee' command as explained by @Bobo or creating a file in a location you do have permissions to and then moving ('mv') it into place.

4
  • Why someone voted down? Is something wrong with this information? Looks quite interesting to me – Loreno Apr 14 at 6:18
  • 2
    Probably because it's flat out wrong. escaping the > as \> is not an alternative to using tee. All it does is change the > from being a redirection operator to being just a string with no special meaning to the shell. – cas Apr 14 at 6:23
  • The > character is part of a quoted string, So it is not interpreted by the local shell before passing it to ssh but by the remote shell before running the sudo command. – Bodo Apr 14 at 12:18
  • Which doesn't solve the permissions problem. The redirection happens in the user's shell, i.e. with the uid of the user running sudo, not as root. That's why something like sudo tee is required. sudo bash -c 'echo ... > filename' would also work, because then the bash process redirecting output to the file is running as root, not as the user who runs sudo. – cas Apr 15 at 3:07

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.