add git deploy key wrapper functionality

This commit is contained in:
Michael Carlson 2020-11-23 21:58:33 -07:00
parent 73d2440ad4
commit 17860381fc
9 changed files with 88 additions and 8 deletions

View file

@ -57,14 +57,20 @@ The `ssh-agent` will load all of the keys and try each one in order when establi
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.
Also, when using **Github deploy keys**, GitHub servers will accept the first known key. But since deploy keys are scoped to a single repository, you might get the error message `fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.` if the wrong key/repository combination is tried.
You might want to [try a wrapper script around `ssh`](https://gist.github.com/mpdude/e56fcae5bc541b95187fa764aafb5e6d) that can pick the right key, based on key comments. See [our blog post](https://www.webfactory.de/blog/using-multiple-ssh-deploy-keys-with-github) for the full story.
In both cases, you might want to [try a wrapper script around `ssh`](https://gist.github.com/mpdude/e56fcae5bc541b95187fa764aafb5e6d) that can pick the right key, based on key comments. See [our blog post](https://www.webfactory.de/blog/using-multiple-ssh-deploy-keys-with-github) for the full story.
Also, when using **Github deploy keys**, GitHub servers will accept the first known key. But since deploy keys are scoped to a single repository, you might get the error message `fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.` if the wrong key/repository combination is tried.
For this scenario, you'll want to set `use-git-deploy-key-wrapper` to `true` and create your key with a comment that has the git SSH url in it. For example:
```
ssh-keygen -t ed25519 -a 100 -C "ssh://git@github.com/ORGANIZATION/REPO.git" -m PEM -N "" -f ~/.ssh/REPO -q
```
**Please note that `git@github.com` is followed by a `/`, not a `:`**
## Exported variables
The action exports the `SSH_AUTH_SOCK` and `SSH_AGENT_PID` environment variables through the Github Actions core module.
The action exports the `SSH_AUTH_SOCK`, `SSH_AGENT_PID` and `GIT_SSH_COMMAND` environment variables through the Github Actions core module.
The `$SSH_AUTH_SOCK` is used by several applications like git or rsync to connect to the SSH authentication agent.
The `$SSH_AGENT_PID` contains the process id of the agent. This is used to kill the agent in post job action.
The `$GIT_SSH_COMMAND` contains the path to the `git-deploy-key-wrapper` shell script. Git will use this value for SSH interactions instead of the SSH executable.
## Known issues and limitations

View file

@ -6,6 +6,9 @@ inputs:
required: true
ssh-auth-sock:
description: 'Where to place the SSH Agent auth socket'
use-git-deploy-key-wrapper:
description: 'Enable usage of git deploy key wrapper for multiple repositories with individual deploy keys'
default: false
runs:
using: 'node12'
main: 'dist/index.js'

View file

@ -5,6 +5,14 @@ try {
// Kill the started SSH agent
console.log('Stopping SSH agent')
execSync('kill ${SSH_AGENT_PID}', { stdio: 'inherit' })
const home = process.env['HOME'];
const homeSsh = `${home}/.ssh`;
const gitSSHWrapperPath = path.join(homeSsh, 'git-deploy-key-wrapper.sh');
if (fs.existsSync(gitSSHWrapperPath)) {
console.log('Removing ssh git SSH wrapper');
fs.unlinkSync(gitSSHWrapperPath);
}
} catch (error) {
console.log(error.message);
console.log('Error stopping the SSH agent, proceeding anyway');

8
dist/cleanup.js vendored
View file

@ -129,6 +129,14 @@ try {
// Kill the started SSH agent
console.log('Stopping SSH agent')
execSync('kill ${SSH_AGENT_PID}', { stdio: 'inherit' })
const home = process.env['HOME'];
const homeSsh = `${home}/.ssh`;
const gitSSHWrapperPath = path.join(homeSsh, 'git-deploy-key-wrapper.sh');
if (fs.existsSync(gitSSHWrapperPath)) {
console.log('Removing ssh git SSH wrapper');
fs.unlinkSync(gitSSHWrapperPath);
}
} catch (error) {
console.log(error.message);
console.log('Error stopping the SSH agent, proceeding anyway');

View file

@ -2,6 +2,7 @@
# The last argument is the command to be executed on the remote end, which is something
# like "git-upload-pack 'webfactory/ssh-agent.git'". We need the repo path only, so we
# Terraform ends up bing "git-upload-pack '/webfactory/ssh-agent.git'"
# loop over this last argument to get the last part of if.
for last in ${!#}; do :; done
@ -12,6 +13,9 @@ trap "rm -f $key_file" EXIT
eval last=$last
# Try to pick the right key
ssh-add -L | grep --word-regexp --max-count=1 $last > $key_file
# No "--word-regexp" because Terraforms usage of git ends up as
# "git-upload-pack 'webfactory/ssh-agent.git'". "--word-regexp" will not match it.
# Other integrations still work without "--word-regexp"
ssh-add -L | grep --max-count=1 $last > $key_file
ssh -i $key_file "$@"
ssh -i $key_file "$@"

9
dist/index.js vendored
View file

@ -163,6 +163,15 @@ try {
console.log("Keys added:");
child_process.execSync('ssh-add -l', { stdio: 'inherit' });
const useGitSSHWrapper = core.getInput('use-git-deploy-key-wrapper');
if(useGitSSHWrapper) {
const gitSSHWrapperFileName = 'git-deploy-key-wrapper.sh';
const gitSSHWrapperPath = path.join(homeSsh, gitSSHWrapperFileName);
fs.copyFileSync(path.join(process.cwd(), gitSSHWrapperFileName), gitSSHWrapperPath);
fs.chmodSync(gitSSHWrapperPath, "755");
core.exportVariable('GIT_SSH_COMMAND', gitSSHWrapperPath);
}
} catch (error) {
core.setFailed(error.message);
}

View file

@ -46,6 +46,15 @@ try {
console.log("Keys added:");
child_process.execSync('ssh-add -l', { stdio: 'inherit' });
const useGitSSHWrapper = core.getInput('use-git-deploy-key-wrapper');
if(useGitSSHWrapper) {
const gitSSHWrapperFileName = 'git-deploy-key-wrapper.sh';
const gitSSHWrapperPath = path.join(homeSsh, gitSSHWrapperFileName);
fs.copyFileSync(path.join(process.cwd(), gitSSHWrapperFileName), gitSSHWrapperPath);
fs.chmodSync(gitSSHWrapperPath, "755");
core.exportVariable('GIT_SSH_COMMAND', gitSSHWrapperPath);
}
} catch (error) {
core.setFailed(error.message);
}

View file

@ -5,9 +5,14 @@ const fs = require('fs')
const buildDir = path.join(process.cwd(), 'build')
const distDir = path.join(process.cwd(), 'dist')
const buildIndexJs = path.join(buildDir, 'index.js')
const distIndexJs = path.join(distDir, 'index.js')
const distCleanupJs = path.join(distDir, 'cleanup.js')
const gitSSHWrapperFileName = 'git-deploy-key-wrapper.sh';
const gitSSHWrapper = path.join(process.cwd(), 'wrapper', gitSSHWrapperFileName);
const buildIndexJs = path.join(buildDir, 'index.js');
const buildGitSSHWrapper = path.join(buildDir, gitSSHWrapperFileName);
const distIndexJs = path.join(distDir, 'index.js');
const distGitSSHWrapper = path.join(distDir, gitSSHWrapperFileName);
const distCleanupJs = path.join(distDir, 'cleanup.js');
if (!fs.existsSync(buildDir)) {
fs.mkdirSync(buildDir)
@ -29,6 +34,13 @@ if (fs.existsSync(distCleanupJs)) {
}
fs.renameSync(buildIndexJs, distCleanupJs)
console.log(`Copying "${gitSSHWrapperFileName}"`);
fs.copyFileSync(gitSSHWrapper, buildGitSSHWrapper);
if (fs.existsSync(distGitSSHWrapper)) {
fs.unlinkSync(distGitSSHWrapper);
}
fs.renameSync(buildGitSSHWrapper, distGitSSHWrapper);
console.log('Cleaning up...')
fs.rmdirSync(buildDir)

View file

@ -0,0 +1,21 @@
#!/bin/bash
# The last argument is the command to be executed on the remote end, which is something
# like "git-upload-pack 'webfactory/ssh-agent.git'". We need the repo path only, so we
# Terraform ends up bing "git-upload-pack '/webfactory/ssh-agent.git'"
# loop over this last argument to get the last part of if.
for last in ${!#}; do :; done
# Don't use "exec" to run "ssh" below; then the trap won't work.
key_file=$(mktemp -u)
trap "rm -f $key_file" EXIT
eval last=$last
# Try to pick the right key
# No "--word-regexp" because Terraforms usage of git ends up as
# "git-upload-pack 'webfactory/ssh-agent.git'". "--word-regexp" will not match it.
# Other integrations still work without "--word-regexp"
ssh-add -L | grep --max-count=1 $last > $key_file
ssh -i $key_file "$@"