[Support Guide] Using an SSH key via environment variable during build

Some workflows require use of an SSH private key during build - for instance, logging in to a server to restart your backend, or using git clone on a private repo during build. There are better ways to accomplish this goal for most use cases, but in case you choose to do that, below is an approach that can work.

First, create an ssh key and add the public key (shorter one) to your service (e.g. on your GitHub repo settings, or in ~/.ssh/authorized_keys on your server)

Then, take the private key (longer one) and transform it into something suitable for the build environment. This means that it wouldn’t have carriage returns in it, so I’ve edited the key I use with this pattern, in my Netlify Build Environment Variables on the Build & Deploy settings page, as shown in this screenshot:

There, I configure my SSH_KEY variable, setting it to a value like this:

-----BEGIN RSA PRIVATE KEY-----_MIIEpAIBAAKCAQEAoCGgoxalJiAF5WKQ...

You can see your own key will look more like:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAoCGgoxalJiAF5WKQ...

So you can see, I’ve just replaced carriage returns with underscores. Then I use this sequence as part of my build command:
mkdir -p ~/.ssh && echo -e "${SSH_KEY//_/\\n}" > ~/.ssh/id_rsa && chmod og-rwx ~/.ssh/id_rsa

Now you’ll have a mostly-usable SSH key in your build environment. I say “mostly” since there are still some things to consider:

  • You’ll need to set these settings for git (which you would & can use manually if you ran ssh interactively): -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no . This stack overflow article shows how to apply them to the git config: https://stackoverflow.com/questions/7772190/passing-ssh-options-to-git-clone
  • You’ll need to do this BEFORE YOUR BUILD STARTS and BEFORE YOU NEED THE KEY. So - this may be a challenge considering you might have a package.json that we try to use with npm install before the build runs. You’ll have to do something to prevent us from running npm instal , such as not having a package.json file in the root of your repo, or make that package optional - and then install it later, AFTER you’ve run those build commands.

I don’t have a great demonstration of that use pattern to share - if you require the package before your build starts, the submodule route mentioned in this article (about hugo, but generally applicable) outlines how this process can happen at clone time rather than post-dependency-installation time. Even without a specific example, hopefully this provides some ideas on how you can approach this process.

1 Like

Just bumped into this issue and was able to find a solution using this article. I created this shell script to execute the necessary steps:

#!/usr/bin/env bash

# Check if we're running in a Netlify environment
# See https://www.netlify.com/docs/continuous-deployment/#environment-variables
if [ ! -z "${DEPLOY_PRIME_URL}" ]; then

    # Init .ssh dir and expand $SSH_KEY
    mkdir -p ~/.ssh
    echo -e "${SSH_KEY//_/\\n}" > ~/.ssh/id_rsa
    chmod og-rwx ~/.ssh/id_rsa

    # Uncomment to debug
    # ls -la ~/.ssh
    # cat ~/.ssh/id_rsa

    # Add host keys, comment out if not needed
    ssh-keyscan -H github.com >> ~/.ssh/known_hosts
    ssh-keyscan -H bitbucket.org >> ~/.ssh/known_hosts

fi;

You can trigger the script using the npm “preinstall” hook:

{
  ...
  "scripts": {
    "preinstall": "bash netlify-setup.sh",
  }
}
4 Likes

thank you for sharing!

Thanks @hongaar!! Great fix!

why not just base64 encode the whole thing? this is a robust approach that doesn’t rely on manual string manipulation, and you can decode right into a buffer, which many crypto libraries expect (yes, it’s a bit silly to encode something that’s already encoded)

As long as it doesn’t get too long, nothing wrong with doing it that way (AWS lambda limits environment variables to 4kbyte total length and a non-encoded SSH key is already pretty long). I am using the initial implementation on a site that doesn’t use node to build, so I did not have an “easy” way to encode/decode except the shell builtins, when I made what I made :slight_smile:

I am not sure if I followed the steps correctly (new to this). My RSA private key is 3.2KB in size. In Netlify UI, I added the environmental variable: SSH_KEY as the key and the private key as the value - the private key value is also surrounded by quotes. In netlify.toml I did this:
[build]
command = “npm install && npm run build && mkdir -p ~/.ssh && echo -e “${SSH_KEY//_/\n}” > ~/.ssh/id_rsa && chmod og-rwx ~/.ssh/id_rsa”

I still got the deploy error: Invalid AWS Lambda parameters. Reserved keys used in this request. Please let me know in steps what to do.

Could you tell us your deploy ID? There are a few reasons you could get that message:

  • you could be trying to use $AWS_SECRET_KEY or $AWS_(some other variable name which we also reserve)
  • But it is likely that your key is just too long as we have an open bug around that error message being delivered for “too long environment” right now, too - how long is it after you base64 encode it? (https://www.base64encode.org/ is the tool I use to test, but do watch out for posting any sensitive information to a random website!)