Automate SSH Key Generation and Deployment

Reading time ~4 minutes

I recently pieced together a bash wrapper script to create, add, and delete an ssh key for temporary use in performing remote tasks over ssh. The processes outlined here assume cURL is available, and that the remote service you wish to connect to has API methods for ssh key handling.

Automating the process of generating and deleting a local ssh key is the easy part. Here’s one way to create a key:

ssh-keygen -q -b 4096 -t rsa -N "" -f ./scripted.key

Options rundown:

  • -b bit strength -> higher than the default 2048
  • -f filename -> easier to subsequently delete the keypair
  • -N passphrase -> empty string
  • -q quiet mode -> no need to review output
  • -t key type -> specify rsa

And now to delete the newly created key-pair:

rm -r ./scripted.key*

Next we’ll set up the API calls to register and delete a public key on remote services, in this case Github and Acquia.


To automate ssh key deployment on Github, you’ll first want to generate a personal access token under ‘Account settings’ > ‘Applications’. We’ll set a variable to the value of the token for easy re-use:


Per the docs, note that the DELETE operation we will eventually employ requires a special admin:public_key permission.

In addition to the token, we’ll set another variable to the value of the ssh public key as follows:

PUBKEY=`cat ./`

Now we can cURL the Github API using the TOKEN and PUBKEY variables. Since we’re setting up for a procedural operation and to reduce the number of network requests, we’ll capture the Github API response (which contains the key ID):

RESPONSE=`curl -s -H "Authorization: token ${TOKEN}" \
  -X POST --data-binary "{\"title\":\"nr@blackhole\",\"key\":\"${PUBKEY}\"}" \`

And now to extract the key ID:

  | grep -o '\"id.*' \
  | grep -o "[0-9]*" \
  | grep -m 1 "[0-9]*"`

Note that the above is more than we really need to be able to parse the Github response. With the Acquia example (coming up next), we’ll see a good reason for setting up the extraction in this manner.

Only one step left, but you may want to add a 10-second sleep to the script to give an opportunity to verify that the key was added before it is deleted.

And now for the delete:

curl -s -H "Authorization: token ${TOKEN}" -X DELETE \${KEYID} \
  -o /dev/null

Here we’re sending the result to /dev/null to ensure the script stays quiet.


Performing this task with Acquia’s Cloud API is much the same, but with a couple of notable differences.

First, we need to set a couple of additional variables:


Variables set, here’s the cURL command to add the key:

RESPONSE=`curl -s -u $CREDS \
  -X POST --data-binary "{\"ssh_pub_key\":\"${PUBKEY}\"}" \"${DOCROOT}"/sshkeys.json?nickname=script`

In this case, we’re going to extract 2 pieces of data from the response. We’ll need the task ID to track the status of adding the key, and we’ll also need the key ID (as with the Github example) so that we can delete the key:

  | grep -o '\"id.*' \
  | grep -o "[0-9]*" \
  | grep -m 1 "[0-9]*"`

  | grep -o "sshkeyid.*" \
  | grep -o "[0-9]*" \
  | grep -m 1 "[0-9]*"`

This is where the utility of the extra bash logic comes in handy, as the Acquia response is condensed JSON, whereas the Github response is readable JSON. Since we don’t have things nicely separated into lines, and since we want to minimize dependencies (this is where I’d otherwise recommend jq), the above gives us what we need with fairly low overhead.

Now to query the task ID so we know when we can start using our key:

until [[ $STATUS =~ ^error|done$ ]]; do
  STATUS=`curl -s -u $CREDS \"${DOCROOT}"/tasks/"${TASKID}".json \
  | grep -o 'state.*' \
  | grep -o '[a-z]*' \
  | sed -n 2p`
  sleep 5

And finally, here’s the delete:

curl -s -u $CREDS -X DELETE \"${DOCROOT}"/sshkeys/"${SSHID}".json \
  -o /dev/null

For reference, I set up a Gist that contains complete bash scripts for both services covered above.


comments powered by Disqus