Strong opinions, weakly held

How do you run SSH from a PHP script?

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.


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

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

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

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

  5. I tried adding -F to the mix as well, thinking that was causing the .ssh directory to be required, but no such luck.

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

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

  8. Queuing them and running them from a cron job is probably the cleanest approach. I am first trying a PHP SSH library first, though.

Leave a Reply

Your email address will not be published.


© 2024 rc3.org

Theme by Anders NorenUp ↑