From 6cf6299d23acd1c656b7bbfe53dcd254ad7c134a Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Tue, 14 Jan 2020 10:21:11 +0100 Subject: [PATCH] Support multiple SSH keys (#14) * Support concatenation of multiple private keys in the given secret * Add a changelog --- .github/workflows/demo.yml | 22 +++++++++++++++----- CHANGELOG.md | 14 +++++++++++++ README.md | 42 +++++++++++++++++++++++++++++--------- dist/index.js | 6 +++++- index.js | 6 +++++- 5 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index 0d860f1..c7c7511 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -1,7 +1,21 @@ on: [push, pull_request] jobs: - load_key_demo: + single_key_demo: + strategy: + matrix: + os: [ubuntu-latest, macOS-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v1 + - name: Setup key + uses: ./ + with: + ssh-private-key: | + ${{ secrets.DEMO_KEY }} + ${{ secrets.DEMO_KEY_2 }} + + multiple_keys_demo: strategy: matrix: os: [ubuntu-latest, macOS-latest] @@ -12,8 +26,6 @@ jobs: uses: ./ with: ssh-private-key: ${{ secrets.DEMO_KEY }} - - run: | - ssh-add -l - echo SSH_AUTH_SOCK is at $SSH_AUTH_SOCK - + + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7d07023 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +* Multiple SSH keys can now be provided (#14, closes #7). Thanks to + @webknjaz and @bradmartin for support and tests. + diff --git a/README.md b/README.md index 59c90e7..81484bb 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ GitHub Actions only have access to the repository they run for. So, in order to ## Usage 1. Create an SSH key with sufficient access privileges. For security reasons, don't use your personal SSH key but set up a dedicated one for use in GitHub Actions. See below for a few hints if you are unsure about this step. -2. In your repository, go to the *Settings > Secrets* menu and create a new secret called `SSH_PRIVATE_KEY`. Put the *unencrypted private* SSH key in `PEM` format into the contents field.
- This key should start with `-----BEGIN RSA PRIVATE KEY-----`, consist of many lines and ends with `-----END RSA PRIVATE KEY-----`. - You can just copy the key as-is from the private key file. -3. In your workflow definition file, add the following step. Preferably this would be rather on top, near the `actions/checkout@v1` line. +2. Make sure you don't have a passphrase set on the private key. +3. In your repository, go to the *Settings > Secrets* menu and create a new secret. In this example, we'll call it `SSH_PRIVATE_KEY`. Put the contents of the *private* SSH key file into the contents field.
+ This key should start with `-----BEGIN ... PRIVATE KEY-----`, consist of many lines and ends with `-----END ... PRIVATE KEY-----`. +4. In your workflow definition file, add the following step. Preferably this would be rather on top, near the `actions/checkout@v1` line. ```yaml # .github/workflows/my-workflow.yml @@ -34,7 +34,28 @@ jobs: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - ... other steps ``` -4. If, for some reason, you need to change the location of the SSH agent socket, you can use the `ssh-auth-sock` input to provide a path. +5. If, for some reason, you need to change the location of the SSH agent socket, you can use the `ssh-auth-sock` input to provide a path. + +### Using multiple keys + +There are cases where you might need to use multiple keys. For example, "deployment keys" might be limited to a single repository each. + +In that case, you can set-up the different keys as multiple secrets and pass them all to the action like so: + +```yaml +# ... contens as before + - uses: webfactory/ssh-agent@v0.1.1 + with: + ssh-private-key: | + ${{ secrets.FIRST_KEY }} + ${{ secrets.NEXT_KEY }} + ${{ secrets.ANOTHER_KEY }} +``` + +The `ssh-agent` will load all of the keys and try each one in order when establishing SSH connections. + +There's one **caveat**, though: SSH servers may abort the connection attempt after a number of mismatching keys have been presented. So if, for example, you have +six different keys loaded into the `ssh-agent`, but the server aborts after five unknown keys, the last key (which might be the right one) will never even be tried. ## Known issues and limitations @@ -72,11 +93,12 @@ As a side note, using `ssh-keyscan` without proper key verification is susceptib ## Creating SSH keys -In order to create a new SSH key, run `ssh-keygen -t rsa -b 4096 -m pem -f path/to/keyfile`. This will prompt you for a key passphrase and save the key in `path/to/keyfile`. +In order to create a new SSH key, run `ssh-keygen -t ed25519 -a 100 -f path/to/keyfile`, as suggested in [this blog post](https://stribika.github.io/2015/01/04/secure-secure-shell.html). +If you need to work with some older server software and need RSA keys, tr `ssh-keygen -t rsa -b 4096 -o -f path/to/keyfile` instead. -Having a passphrase is a good thing, since it will keep the key encrypted on your disk. When configuring the secret `SSH_PRIVATE_KEY` value in your repository, however, you will need the private key *unencrypted*. - -To show the private key unencrypted, run `openssl rsa -in path/to/key -outform pem`. +Both commands will prompt you for a key passphrase and save the key in `path/to/keyfile`. +In general, having a passphrase is a good thing, since it will keep the key encrypted on your disk. When using the key with this action, however, you need to make sure you don't +specify a passphrase: The key must be usable without reading the passphrase from input. Since the key itself is stored using GitHub's "Secret" feature, it should be fairly safe anyway. ## Authorizing a key @@ -93,7 +115,7 @@ As a note to my future self, in order to work on this repo: * Clone it * Run `npm install` to fetch dependencies * _hack hack hack_ -* `node index.js` (inputs are passed through `INPUT_` env vars, but how to set `ssh-private-key`?) +* `node index.js`. Inputs are passed through `INPUT_` env vars with their names uppercased. Use `env "INPUT_SSH-PRIVATE-KEY=\`cat file\`" node index.js` for this action. * Run `./node_modules/.bin/ncc build index.js` to update `dist/index.js`, which is the file actually run * Read https://help.github.com/en/articles/creating-a-javascript-action if unsure. * Maybe update the README example when publishing a new version. diff --git a/dist/index.js b/dist/index.js index 2ae7540..602df64 100644 --- a/dist/index.js +++ b/dist/index.js @@ -72,7 +72,11 @@ try { core.exportVariable('SSH_AUTH_SOCK', authSock); console.log("Adding private key to agent"); - child_process.execSync('ssh-add -', { input: core.getInput('ssh-private-key') }); + core.getInput('ssh-private-key').split(/(?=-----BEGIN)/).forEach(function(key) { + child_process.execSync('ssh-add -', { input: key.trim() + "\n" }); + }); + console.log("Keys added:"); + child_process.execSync('ssh-add -l', { stdio: 'inherit' }); } catch (error) { core.setFailed(error.message); } diff --git a/index.js b/index.js index 141ad08..140afb4 100644 --- a/index.js +++ b/index.js @@ -17,7 +17,11 @@ try { core.exportVariable('SSH_AUTH_SOCK', authSock); console.log("Adding private key to agent"); - child_process.execSync('ssh-add -', { input: core.getInput('ssh-private-key') }); + core.getInput('ssh-private-key').split(/(?=-----BEGIN)/).forEach(function(key) { + child_process.execSync('ssh-add -', { input: key.trim() + "\n" }); + }); + console.log("Keys added:"); + child_process.execSync('ssh-add -l', { stdio: 'inherit' }); } catch (error) { core.setFailed(error.message); }