I've been struggling to crontab automate a git push process with ssh, and it's proving tricky to setup a key with the ssh agent. Starting with a basic script to test the agent:

# set paths (all examples)
source /etc/profile
export PATH=$PATH:/Users/myusername/.ssh

eval "$(ssh-agent)" > ssh_agent.txt
/usr/bin/ssh-add -l >> ssh_agent.txt

..which outputs the following:

Agent pid 16062
The agent has no identities.

So the agent is active. Now to add the key and check it's registered with the agent. In iTerm2 manually this works ok:

/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
2048 [key fingerprint] /Users/myusername/.ssh/id_rsa (RSA)

I try to replicate this inside a crontab job, using this script:

/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
/usr/bin/ssh-add -l >> ssh_agent.txt

..which reports The agent has no identities to file, and the following via "You have mail in /var/mail/myusername":

[email metadata]
Enter passphrase for /Users/myusername/.ssh/id_rsa: 

So the key is trying to load but is catching at the passphrase prompt. Here I want to use expect to give it the passphrase, as described in this unix.stackexchange answer (which looks like it should work in OSX):

#!/usr/bin/expect -f
/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
expect "Enter passphrase for /Users/myusername/.ssh/id_rsa:"
send "mypassphrase\n";

The mail error response is:

Enter passphrase for /Users/myusername/.ssh/id_rsa: 
couldn't read file "Enter passphrase for /Users/myusername/.ssh/id_rsa:": no such file or directory
/Users/myusername/test/crontest.sh: line 17: send: command not found
/Users/myusername/test/crontest.sh: line 18: interact: command not found

So I tried an alternative configuration based on this SO answer:

#!/usr/bin/expect -f
expect -c "
/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
expect \"Enter passphrase for /Users/myusername/.ssh/id_rsa: \"
send \"mypassphrase\n\";

.. and get the following mail error report:

invalid command name "/usr/bin/ssh-add"
    while executing
"/usr/bin/ssh-add /Users/myusername/.ssh/id_rsa"
Could not open a connection to your authentication agent.

I don't understand the problem here - ssh agent works and the key seems accessible so what is this 'connection' issue? I've tried other configurations for expect without success. Very grateful for assistance.

  • N.B. eval "$(ssh-agent)" needs to be included in a working script as that actually initiates the agent. – geotheory Mar 27 '15 at 13:13

I wouldn’t recommend running ssh-add or expect in a cron job as it’s excessively complicated to run it in the limited environment that cron uses for executing its jobs and I’d prefer not to store passwords in the crontab.

Rather than using your regular SSH key, I’d create a new key that is only ever to be used to run the command you want to be run by cron. I’d configure the key without a pass-phrase and change its ownership / permissions (0600) so that it can only be accessed by the effective user ID that the cron job runs as, i.e., the ID of the users whose crontab is being processed.

In your specific case, the specific command is git push so the new key would have to be uploaded to your git server. Most git servers should be able to accept multiple public SSH keys.

In general, for automated processes, it’s best to create specific keys without pass-phrases. A restricted set of allowed commands is then configured on the remote SSH server by prepending option pairs to its line in the authorized_keys file, e.g.,

command="automated-task.sh",client=my.local.host,no-pty,no-agent-forwarding,no-port-forwarding ssh-rsa AAA…public…part…of…specific…keypair automated_user@server

Disclaimer: I have more experience with GNU/Linux than OS X but AFAIK the above information should apply for all *nix systems.

  • That works nicely. Thanks Anthony. Would you say the default OSX chmod 600 is sufficient for a private key? – geotheory Mar 27 '15 at 12:32
  • 1
    @geotheory Glad to hear it. 600 sounds just right. – Anthony Geoghegan Mar 27 '15 at 12:42
  • I've just updated my answer to include the file permissions. – Anthony Geoghegan Mar 27 '15 at 12:55

Alternate answer

I just thought I’d take a closer look at your expect scripts and I noticed that they’re missing the spawn command:

#!/usr/bin/expect -f
spawn /usr/bin/ssh-add /Users/myusername/.ssh/id_rsa
expect "Enter passphrase for /Users/myusername/.ssh/id_rsa:"
send "mypassphrase\n";

From the expect manual:

spawn [args] program [args]

creates a new process running program args. Its stdin, stdout and stderr are connected to Expect, so that they may be read and written by other Expect commands. The connection is broken by close or if the process itself closes any of the file identifiers.

  • I got "spawn: command not found", which looks like bash is wrong interpreter, which explains the rationale for the 2nd method I tried. So I tried that version with 'spawn', which tantalisingly returns: spawn /usr/bin/ssh-add /Users/myusername/.ssh/id_rsa Enter passphrase for /Users/myusername/.ssh/id_rsa: However, still "The agent has no identities", so something is still amiss. – geotheory Mar 27 '15 at 15:08

This answer has another working solution:

If you're using ssh-add to add your key(s) to the ssh agent, you can add the SSH_AUTH_SOCK environment variable to your crontab, and ssh commands there will be able to use the keys already loaded and unlocked.

  1. In a regular terminal session, load your ssh-agent (this is left as an exercise to the reader; I think on macOS it is running by default, or you can run ssh-add -t 24h ~/path/to/your.key).
  2. Get your SSH_AUTH_SOCK location with env | grep SSH_AUTH_SOCK
  3. Add this to the top of your crontab, e.g., SSH_AUTH_SOCK=/Users/USER/.ssh/ssh_auth_sock.

If you haven't loaded or authenticated keys, the crontab ssh commands will still fail, but for me it's fine since 99% of the time I have keys loaded.

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.