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

Last checked by Netlify Support Team: December 2022

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 install , 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.

3 Likes

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",
  }
}
5 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)

3 Likes

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!)

Thanks for all the tips above. I’m still having some trouble and wondering if someone has any ideas for this:

I am using an express server wrapped in serverless-http attempting to deploy a function on Netlify. It was working beautifully until I added authentication with Auth0, which was accomplished using some express JWT middleware. Now whenever I try to hit my endpoint, I get the following errors:

  • On the client:
"error": "Unexpected token < in JSON at position 0"
  • And in the netlify function logs:
UnauthorizedError: error:0909006C:PEM routines:get_name:no start line
at /var/task/src/node_modules/express-jwt/lib/index.js:105:22

I have pretty much narrowed the issue down to my SSH_KEY certificate in the environment variable (reason being I can run the function locally with no problems). It is less than 1kb, and my other variables are small so I don’t think I’m running into the 4kb limit on lambda.

I have implemented the solution from @hongaar above using “underscores instead of carriage returns”, the bash script and the npm preinstall hook, but I’m still getting the same error. I believe my issue is around what @fool was saying in the original post: I need to run the script BEFORE the build starts… and my log is showing this (my build command is running and then the bash script is running:

The certificate looks good though! The script seems to be working, I think it’s just not running at the right time… Am I in the right ballpark? Any suggestions?

This is the way. Thanks @parkan! I thought it was kinda silly too, but encoding to base64 produces a simple string with no special characters, which Netlify seems to prefer :+1:

Also no need for the bash scripts and build config. Just decode in your server:
Buffer.from(process.env.SSH_KEY, 'base64').toString('utf8')

1 Like

This worked for me too! I was having trouble for two days and came across this post. I encoded the private key to Base64, added that to my environment variables, and then prior to using it in my code I decoded it back to utf8.

Buffer.from('Key Goes Here').toString('base64');

thanks for sharing this with the community