«    »

Automated Deploys using SSH and Ant

I am a big proponent of automating the building and deployment of application code: this minimizes the chance of human error and ensures a consistent process is followed. Last year I wrote an article about automatically deploying code via FTP using the Java-based Apache Ant build tool. Since then I have needed to deploy to servers which do not have FTP enabled for security reasons. The alternative is to use SSH, a secure network protocol which supports the transfer of files via SFTP or SCP. This article describes how to automate deployments using SSH and Ant.

Ant ships with two relevant tasks: <scp> and <sshexec>. Both of these tasks require the third-party library Java Secure Channel (Jsch) to provide a Java implementation of the SSH protocol. The jsch.jar file should be placed in the /lib directory of your Ant installation. While these tasks support both the SSH 1 and SSH 2 protocols, SSH 2 is preferred because it is much more secure. Use of SSH 2, however, requires Ant version 1.7.0 or greater.

Using the <scp> task appears at first glance to be quite similar to the <ftp> task. Here is an example:

<scp remoteTodir="${user}@${server}:${target.dir}"
  password="${password}"
  trust="yes"
  sftp="true">
    <fileset dir="${source.dir}">
      <include name="**/*"/>
    </fileset>
</scp>

This transfers files from ${source.dir} on your machine running the Ant build to the directory ${target.dir} on the machine ${server}. Despite the similarity to the <ftp> task, there are several significant differences:

  • SCP transfers files as binary copies only and has no support for the ASCII transfer mode of FTP. This means that transferring text files between operating systems (Windows, Unix, Mac) can cause problems as the line ending characters vary across the three operating systems. The solution is to use the <fixcrlf> task to convert the line endings of text files before or after they are transferred.
  • SCP transfers all the files in the fileset every time, whereas the FTP task supports transferring only newer files.
  • SCP provides no mechanism for setting the file access permissions of newly created or existing files. On UNIX operating systems, SCP grants read & write access to the file owner and no permissions for the group or the world (other). These restrictive permissions are seldom appropriate – executable files should have the execute flag set and often the group needs read & execute permission. The solution I use to fix the permissions is to run a script on the target server using the <sshexec> task after the <scp> task to fix the permissions on the files & directories. This script is maintained as part of the code base and transferred to the target server via SCP along with the rest of the code. Below is an example of using <sshexec> to do this.
<sshexec host="${server}"
  username="${user}"
  password="${password}"
  trust="yes"
  command="perl -W ${target.dir}/bin/post_deploy_configuration.pl"
/>

One capability that the <scp> and <sshexec> tasks have that the <ftp> task lacks is the ability to use public key authentication instead of passwords. One significant advantages of using keys instead of passwords for an automated build is that keys do not expire, whereas passwords are often required to expire regularly. Keys are also much more secure in situations when the server or the network between the build machine and the server is at risk of being compromised.

The downside to using public key authentication is that some up-front work is required to set up and install the keys. The necessary steps are:

  • Create a new key pair, either using the SSH software on your server or using an utility such as PuTTYgen. You should end up with a file holding the public key, a file holding the private key, and a passphrase for accessing the private key.
  • Ensure the private key is stored in the openssh format, which is what the Jsch library used by the <scp> and <sshexec> tasks requires. PuTTYgen, for example, by default stores private keys in PuTTY's format but can convert a key to openssh format.
  • Install the public key on the target server under the user id being used by the build. This involves copying the public key file into a specially-named subdirectory of the user's home directory and adding an entry listing the public key file to a special file within this subdirectory. The name of the subdirectory and special file depends on the specific operating system and specific version of SSH being used.
  • Make the private key available to the build machine.
  • Change your build.xml to include the following attributes for the <scp> and <sshexec> tasks instead of the password attribute: keyfile="${key.file}" passphrase="${passphrase}". Just like with passwords, do not embed the passphrase directly within build.xml. I store it in a property file which along with the private key is stored in a secured non-public location available to the build machine.

The <sshexec> task is limited to executing a single command on the server. The SSHTools project provides a more powerful <ssh> Ant task that supports automating an interactive remote session. Below is an example:

<ssh host="${server}"
  username="${user}"
  password="${password}"
  verifyhost="false"
>
  <shell term="vt100">
    <write>whoami</write>
    <read timeout="50000">${user}</read>
    <write>chmod -R u+rwx,g+rx ${target.dir}</write>
    <write>echo Done</write>
    <read timeout="50000">Done</read>
  </shell term="vt100">
</ssh>

If you find this article helpful, please make a donation.

8 Comments on “Automated Deploys using SSH and Ant”

  1. Basil –

    If you haven’t already, I would suggest checking out Capistrano (http://www.capify.org). It’s primarily a tool for deploying Rails applications, but it can be used for other deployment and systems administration tasks as well.

    It sounds like you have a solution that works for you, I just wanted to throw it out there.

  2. Kurt says:

    Thank you very much!

    I am looking to do the exact same thing that you describe in this article. It took me a while to find this. It should have been the first hit on Google!

    If you don’t mind, what suggestions do you have for reading in usernames and passwords? I have been fighting with Ant to find a decent way to do this. I can use the input task obviously but the password is displayed in cleartext. I can’t seem to find a way to suppress that. In fact, it’s how I stumbled upon your article. Again, many thanks for the great article.

  3. Hi Kurt,

    For managing passwords/usernames, I like to read them in from a properties file that is not stored in version control, but is typically stored in a secured directory (i.e. user’s home directory).

  4. kiran kumar says:

    Hi,
    I am automating a deployment process for which i need to run some commands on a remote server. For this i think i need to use sshexec in ant script. but how to configure this so that i can start using this task in ant script? Configuration in the sense like adding jch jar and so on. Can anyone clearly mention the steps that needs to be done so that i can use sshexec in ant script?

  5. @kiran, the article shows a sample invocation of sshexec from ant – see the second block of ant code.

  6. Felix says:

    Thank you very much. Search for such auto deployment things for long time. that’s really work for my project.

  7. Anton says:

    the supports more than 1 command.. you just need to seperate them with ; between each command.

    if your target remote machine is a unix/linux machine, then this will work.
    if its windows, i believe either this or multiple command nested arguments will work.

  8. Jym says:

    =v= Since I can’t use ssh-agent with ant, and I won’t put plaintext passwords or passphrases in files, including shell history files, I use this approach:

    $ read ANT_ARGS
    -Dpassphrase=YourPassphraseShouldBeMoreSecureThanThis

    Should I want to pass anything else along to ant, I set up my .antrc like this:

    ANT_ARGS=”$ANT_ARGS -Dfoo=bar -Dand.so.on=and.so.forth”

    (It’s best not to export ANT_ARGS.)

    For builds inside Emacs, setting ANT_ARGS with M-x setenv works just as well.

«    »