I’m writing a very simple deployment script that logs into a remote server and uses Git to pull the latest code from the remote repository. The deployment application runs on Server A, and it will update code on Server B and Server C.
The deployment application is written in PHP, and it’s easy enough to call the ssh
command using PHP’s shell_exec()
function. Well, calling SSH is easy, but making it work is a bit more difficult.
SSH really wants to run as a user who has a .ssh
directory in their home directory. First, it needs to find the private key in order to authenticate against the remote server. You can get around that using the -i
flag and pointing at a specific key file. You also need to indicate to SSH that you don’t care about the host key (which prevents you from being victimized by man-in-the-middle attacks) or to point it to your known_hosts
file. You can specify a custom location for that using the -o
option, like this:
-o UserKnownHostsFile=keys/known_hosts
Even after that, though, SSH still insists on using a .ssh
directory for the user running the command, in this case, the Apache user. Creating such a folder doesn’t seem like it should be necessary, but I haven’t been able to figure out how to avoid it.
If I can’t get this to work, I could try using the SSH2 library for PHP, but I’d prefer not to, since I don’t want to deal with the added dependency.
I’ve posted this question on Stack Overflow as well.
February 24, 2012 at 6:21 am
You should investigate the -F flag, which allows you to directly specify the SSH config file. Use ‘man ssh’ for details. Using it, possibly along with -i if you need a specific identity file, will work around the problem.
February 24, 2012 at 8:31 am
I’m looking forward to seeing how this works out. Please post a follow-up when you have it working. I’m a one-person shop getting ready to expand in the next year, and one of the big issues I’m going to need to deal with is developing a deployment script similar to what you seem to be doing. I have a core CMS running several dozen client sites, and I have an ad hoc deployment process right now using Transmit with a bunch of rules. It works fine when it’s just me, but add another developer into the mix and I need a real solution.
February 24, 2012 at 12:13 pm
At least you don’t need to automate password-based SSH (a terrible idea). I haven’t found a way to do that without using the Expect extension to TCL.
Options you might find useful: -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o CheckHostIP=no
February 24, 2012 at 12:16 pm
IIRC, disabling CheckHostIP is useful in an environment where you have a pool of IPs but are continuously bringing up and taking down new machines using the same limited pool of IPs. Normally, OpenSSH would begin complaining about man-in-the-middle attacks at this point.
February 24, 2012 at 12:35 pm
I tried adding -F to the mix as well, thinking that was causing the .ssh directory to be required, but no such luck.
February 27, 2012 at 8:21 am
Drat. The best alternative then it to use either SSH2 or phpseclib library, which both have the benefit of being somewhat safer than shelling out to boot.
February 27, 2012 at 11:53 pm
As I mentioned on Twitter, my first choice is to put things like this into a (database) queue and execute ’em with a cron job, but how about a sete?[ug]id C wrapper script that fires up bash with a sourced ~/.bashrc and runs your SSH that way?
February 28, 2012 at 1:39 am
Queuing them and running them from a cron job is probably the cleanest approach. I am first trying a PHP SSH library first, though.