Skip to content

Commit ba14c02

Browse files
authored
Support Existing Agents + Purge Entries (#45)
* docs: adjust readme for new property * build: upgrade to latest deps * feat: support existing agent + purge * feat: add purge entry
1 parent fa571b7 commit ba14c02

File tree

4 files changed

+533
-451
lines changed

4 files changed

+533
-451
lines changed

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
# SSH Socket Setup GitHub Action
2-
Setup an SSH socket with a private key.
2+
_Set up (or re-use) an SSH socket with a private key._
33

44
## Usage
55
### Inputs
66

77
Following inputs can be used as `step.with` keys
88

9-
| Name | Required | Type | Description |
10-
|---------------|----------|--------|------------------------------------|
11-
| `host` | Yes | String | Remote hostname. |
12-
| `port` | No | Number | SSH Port (default: `22`). |
13-
| `socket-path` | No | String | Path at which to create socket. |
14-
| `key` | Yes | String | base64 private key |
15-
| `lifetime` | No | Number | Seconds to keep key (default: 600) |
9+
| Name | Required | Type | Description |
10+
|---------------|----------|---------|----------------------------------------------------|
11+
| `host` | Yes | String | Remote hostname. |
12+
| `port` | No | Number | SSH Port (default: `22`). |
13+
| `socket-path` | No | String | Path at which to create socket. |
14+
| `key` | Yes | String | base64 private key |
15+
| `lifetime` | No | Number | Seconds to keep key (default: 600) |
16+
| `purge-entry` | No | Boolean | Purge `known_hosts` entry for host (default: true) |
1617

1718
You may encode your private key in base64 via:
1819

action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ inputs:
2323
description: 'how many seconds to keep socket live'
2424
required: false
2525
default: '600'
26+
purge-entry:
27+
description: 'Whether to purge the host entry from known_hosts prior to addition.'
28+
required: false
29+
default: 'true'
2630
outputs:
2731
socket-path:
2832
description: 'path at which socket was created'

index.js

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,69 @@
11
const core = require('@actions/core');
2-
32
const { execSync } = require('child_process');
43

54
const host = core.getInput('host');
65
const port = core.getInput('port');
76
const key = core.getInput('key');
87
const lifetimeInSeconds = core.getInput('lifetime');
8+
const shouldPurgeEntry = core.getBooleanInput('purge-entry', {required: false});
99
let socketPath = core.getInput('socket-path');
1010

11-
// Create random socket path, if none passed.
12-
if (!socketPath) {
13-
try {
14-
socketPath = execSync('mktemp -u', {encoding: 'utf-8'}).trim();
15-
} catch (e) {
16-
core.setFailed(e.message);
17-
process.exit(1);
18-
}
19-
}
20-
21-
console.log(`Attempting to create ${socketPath}...`);
11+
// Check if we already have a pid & sock.
12+
const pid = process.env.SSH_AGENT_PID;
13+
const sock = process.env.SSH_AUTH_SOCK;
2214

2315
// Prepare the host file.
2416
execSync('mkdir -p ~/.ssh');
2517
execSync('touch ~/.ssh/known_hosts');
26-
execSync(`sed -i -e '/^${host} /d' ~/.ssh/known_hosts`);
27-
execSync(`ssh-keyscan${port ? ` -p ${port}` : ''} "${host}" >> ~/.ssh/known_hosts`);
2818

29-
// Start the agent (or re-use one)
30-
try {
31-
execSync(`ssh-agent -a "${socketPath}"`)
32-
} catch (e) {
33-
if (e.message.includes('Address already in use')) {
34-
core.info('Agent already exists on sock. Skipping creation.');
35-
} else {
36-
core.setFailed(e.message);
37-
process.exit(1);
38-
}
39-
}
19+
if (pid) {
20+
core.info('SSH Agent already running. Skipping spawn of ssh-agent...');
4021

41-
// Pluck the pid and set values (if possible)
42-
try {
43-
const pid = parseInt(execSync(`fuser ${socketPath} 2> /dev/null`, {encoding: 'utf-8'}));
22+
core.exportVariable('SSH_AUTH_SOCK', sock);
23+
core.setOutput('socket-path', sock);
4424
core.exportVariable('SSH_AGENT_PID', pid);
4525
core.setOutput('agent-pid', pid);
46-
} catch (e) {
47-
core.warning('PID capture failed (fuser). Skipping...');
26+
} else {
27+
// Create random socket path, if none passed.
28+
if (!socketPath) {
29+
try {
30+
socketPath = execSync('mktemp -u', {encoding: 'utf-8'}).trim();
31+
} catch (e) {
32+
core.setFailed(e.message);
33+
process.exit(1);
34+
}
35+
}
36+
37+
console.log(`Attempting to create ${socketPath}...`);
38+
39+
try {
40+
execSync(`ssh-agent -a "${socketPath}"`)
41+
} catch (e) {
42+
if (e.message.includes('Address already in use')) {
43+
core.info('Agent already exists on sock. Skipping creation.');
44+
} else {
45+
core.setFailed(e.message);
46+
process.exit(1);
47+
}
48+
}
49+
50+
// Pluck the pid and set values (if possible)
51+
try {
52+
const pid = parseInt(execSync(`fuser ${socketPath} 2> /dev/null`, {encoding: 'utf-8'}));
53+
core.exportVariable('SSH_AGENT_PID', pid);
54+
core.setOutput('agent-pid', pid);
55+
} catch (e) {
56+
core.warning('PID capture failed (fuser). Skipping...');
57+
}
58+
59+
core.exportVariable('SSH_AUTH_SOCK', socketPath);
60+
core.setOutput('socket-path', socketPath);
4861
}
4962

50-
// Add the key and set outputs
51-
core.exportVariable('SSH_AUTH_SOCK', socketPath);
52-
core.setOutput('socket-path', socketPath);
63+
if (shouldPurgeEntry) {
64+
execSync(`sed -i -e '/^${host} /d' ~/.ssh/known_hosts`);
65+
}
66+
execSync(`ssh-keyscan${port ? ` -p ${port}` : ''} "${host}" >> ~/.ssh/known_hosts`);
67+
5368
execSync(`echo "${key}" | base64 -d | ssh-add -t ${lifetimeInSeconds} -`);
5469
core.info('Done; exiting.');

0 commit comments

Comments
 (0)