Skip to content

Commit 7c08ee6

Browse files
Merge pull request #15 from aligent/feature/ssh-configuration
JAN-1120: Setup SSH configuration
2 parents 7bbad22 + 5808dd6 commit 7c08ee6

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

bin/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
getInstallCommand,
1111
} from '../lib/packageManagers';
1212
import { isNxServerlessMonorepo } from '../lib/serverlessProjectType';
13+
import { setupSshCredentials } from '../lib/ssh';
1314
import { uploadDeploymentBadge } from '../lib/uploadDeploymentBadge';
1415

1516
async function main() {
@@ -34,6 +35,9 @@ async function main() {
3435
);
3536
}
3637

38+
// Setup SSH credentials for the pipeline, these are generated by Bitbucket Pipelines
39+
await setupSshCredentials();
40+
3741
const packageManager = detectPackageManager(bitbucketCloneDir);
3842
const isMonorepo = await isNxServerlessMonorepo(bitbucketCloneDir);
3943

lib/ssh.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import fs from 'fs';
2+
import os from 'os';
3+
4+
/**
5+
* Sets up the SSH credentials for the pipeline.
6+
* This function copies the SSH identity file and known hosts file from the Bitbucket Pipelines agent to the local .ssh directory.
7+
* It also updates the SSH configuration file to include the identity file.
8+
*
9+
* @returns {Promise<void>} Resolves when the SSH credentials are setup or we fail to update the SSH configuration file (which doesn't fail the pipeline)
10+
*/
11+
export async function setupSshCredentials(): Promise<void> {
12+
const homeDir = os.homedir();
13+
const sshDir = `${homeDir}/.ssh/`;
14+
const sshConfigDir = `/opt/atlassian/pipelines/agent/ssh`;
15+
const identityFile = `${sshConfigDir}/id_rsa_tmp`;
16+
const knownHostsFile = `${sshConfigDir}/known_hosts`;
17+
18+
// Ensure the SSH directory exists
19+
const sshDirExists = await fs.promises
20+
.stat(sshDir)
21+
.then((stat) => stat.isDirectory());
22+
if (!sshDirExists) {
23+
await fs.promises.mkdir(sshDir, { recursive: true });
24+
}
25+
26+
// Copy over the SSH identity file that Bitbucket has generated, if this fails then we should fail the whole pipeline
27+
try {
28+
console.log('Attempting to copy SSH identity file...');
29+
30+
const pipelinesIdFile = `${sshDir}/pipelines_id`;
31+
await fs.promises.copyFile(identityFile, pipelinesIdFile);
32+
33+
console.log(`Copied to ${pipelinesIdFile}`);
34+
console.log(`Adding identity file config to config file`);
35+
36+
const configFile = `${sshDir}/config`;
37+
await fs.promises.appendFile(
38+
configFile,
39+
`IdentityFile ${pipelinesIdFile}`
40+
);
41+
} catch (e) {
42+
console.error(
43+
'Failed to update SSH configuration, check that SSH key configuration in Pipelines is valid. \n Check Pipelines -> SSH Keys.'
44+
);
45+
return;
46+
}
47+
48+
// Copy over the known_hosts file that Bitbucket generated
49+
try {
50+
console.log('Piping known hosts into runtime ssh config');
51+
const knownHosts = await fs.promises
52+
.readFile(knownHostsFile)
53+
.then((buf) => buf.toString());
54+
const hostsFile = `${sshDir}/known_hosts`;
55+
await fs.promises.appendFile(hostsFile, knownHosts);
56+
} catch (e) {
57+
console.error(
58+
'Failed to update hosts file. \n Check Pipelines configuration for known hosts.'
59+
);
60+
return;
61+
}
62+
63+
console.log('Updating SSH directory permissions');
64+
65+
await chmodRecursive(sshDir, 0o700);
66+
}
67+
68+
/**
69+
* Recursively changes the permissions of a directory and its contents.
70+
*
71+
* @param {fs.PathLike} path - The path to the directory to change permissions for.
72+
* @param {fs.Mode} mode - The mode to set for the directory and its contents.
73+
* @throws {Error} If the directory or its contents cannot be changed.
74+
*/
75+
async function chmodRecursive(path: fs.PathLike, mode: fs.Mode): Promise<void> {
76+
await fs.promises.chmod(path, mode);
77+
78+
const entries = await fs.promises.readdir(path, {
79+
withFileTypes: true,
80+
});
81+
82+
for (const entry of entries) {
83+
const fullPath = `${path}/${entry.name}`;
84+
if (entry.isDirectory()) {
85+
await chmodRecursive(fullPath, mode);
86+
} else {
87+
await fs.promises.chmod(fullPath, mode);
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)