Skip to content

Commit fa719d6

Browse files
author
Stijn Seghers
authored
Merge pull request #2 from NarrativeApp/feat/ssm-parameter-store
feat: use SSM Parameter Store to store secrets
2 parents 797fb7d + 6dff0e9 commit fa719d6

File tree

13 files changed

+180
-180
lines changed

13 files changed

+180
-180
lines changed

modules/runners/encrypt.tf

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
import { scaleUp } from './scale-runners/scale-up';
2-
import { scaleDown } from './scale-runners/scale-down';
1+
import { scaleUp as scaleUp_ } from './scale-runners/scale-up';
2+
import { scaleDown as scaleDown_ } from './scale-runners/scale-down';
33
import { SQSEvent, ScheduledEvent, Context } from 'aws-lambda';
44

5-
module.exports.scaleUp = async (event: SQSEvent, context: Context, callback: any) => {
5+
export async function scaleUp(event: SQSEvent, context: Context, callback: any) {
66
console.dir(event, { depth: 5 });
77
try {
88
for (const e of event.Records) {
9-
await scaleUp(e.eventSource, JSON.parse(e.body));
9+
await scaleUp_(e.eventSource, JSON.parse(e.body));
1010
}
1111
return callback(null);
1212
} catch (e) {
1313
console.error(e);
1414
return callback('Failed handling SQS event');
1515
}
16-
};
16+
}
1717

18-
module.exports.scaleDown = async (event: ScheduledEvent, context: Context, callback: any) => {
18+
export async function scaleDown(event: ScheduledEvent, context: Context, callback: any) {
1919
try {
20-
scaleDown();
20+
scaleDown_();
2121
return callback(null);
2222
} catch (e) {
2323
console.error(e);
2424
return callback('Failed');
2525
}
26-
};
26+
}

modules/runners/lambdas/runners/src/scale-runners/gh-auth.test.ts

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { createOctoClient, createGithubAuth } from './gh-auth';
22
import nock from 'nock';
33
import { createAppAuth } from '@octokit/auth-app';
44
import { StrategyOptions } from '@octokit/auth-app/dist-types/types';
5-
import { decrypt } from './kms';
5+
import SSM from './ssm';
66
import { RequestInterface } from '@octokit/types';
77
import { mock, MockProxy } from 'jest-mock-extended';
88
import { request } from '@octokit/request';
99

10-
jest.mock('./kms');
1110
jest.mock('@octokit/auth-app');
11+
jest.mock('./ssm');
1212

1313
const cleanEnv = process.env;
1414

@@ -19,7 +19,7 @@ beforeEach(() => {
1919
nock.disableNetConnect();
2020
});
2121

22-
describe('Test createGithubAuth', () => {
22+
describe('Test createOctoClient', () => {
2323
test('Creates app client to GitHub public', async () => {
2424
// Arrange
2525
const token = '123456';
@@ -46,56 +46,67 @@ describe('Test createGithubAuth', () => {
4646
});
4747

4848
describe('Test createGithubAuth', () => {
49-
const mockedDecrypt = (decrypt as unknown) as jest.Mock;
49+
const mockedSSM = SSM as jest.MockedClass<typeof SSM>;
5050
const mockedCreatAppAuth = (createAppAuth as unknown) as jest.Mock;
5151
const mockedDefaults = jest.spyOn(request, 'defaults');
5252
let mockedRequestInterface: MockProxy<RequestInterface>;
5353

5454
const installationId = 1;
5555
const authType = 'app';
5656
const token = '123456';
57-
const decryptedValue = 'decryptedValue';
58-
const b64 = Buffer.from(decryptedValue, 'binary').toString('base64');
57+
const privateKey = 'my-private-key';
58+
const privateKeyBase64 = Buffer.from(privateKey, 'binary').toString('base64');
59+
const appId = '123';
60+
const clientId = 'abc';
61+
const clientSecret = 'abcdef123456';
5962

6063
beforeEach(() => {
61-
process.env.GITHUB_APP_ID = '1';
62-
process.env.GITHUB_APP_CLIENT_SECRET = 'client_secret';
63-
process.env.GITHUB_APP_KEY_BASE64 = 'base64';
64-
process.env.KMS_KEY_ID = 'key_id';
64+
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
65+
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
66+
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
67+
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
6568
process.env.ENVIRONMENT = 'dev';
66-
process.env.GITHUB_APP_CLIENT_ID = '1';
6769
});
6870

6971
test('Creates auth object for public GitHub', async () => {
7072
// Arrange
7173
const authOptions = {
72-
appId: parseInt(process.env.GITHUB_APP_ID as string),
73-
privateKey: 'decryptedValue',
74+
appId: parseInt(appId),
75+
privateKey,
7476
installationId,
75-
clientId: process.env.GITHUB_APP_CLIENT_ID,
76-
clientSecret: 'decryptedValue',
77+
clientId,
78+
clientSecret,
7779
};
7880

79-
mockedDecrypt.mockResolvedValueOnce(decryptedValue).mockResolvedValueOnce(b64);
81+
const mockedGetParameter = jest.fn()
82+
.mockResolvedValueOnce(privateKeyBase64)
83+
.mockResolvedValueOnce(appId)
84+
.mockResolvedValueOnce(clientId)
85+
.mockResolvedValueOnce(clientSecret);
86+
mockedSSM.mockImplementation(() => ({
87+
ssm: null as any,
88+
getParameter: mockedGetParameter,
89+
}));
90+
8091
const mockedAuth = jest.fn();
8192
mockedAuth.mockResolvedValue({ token });
82-
mockedCreatAppAuth.mockImplementation((authOptions: StrategyOptions) => {
83-
return mockedAuth;
84-
});
93+
mockedCreatAppAuth.mockImplementation(() => mockedAuth);
8594

8695
// Act
8796
const result = await createGithubAuth(installationId, authType);
8897

8998
// Assert
90-
expect(mockedDecrypt).toBeCalledWith(
91-
process.env.GITHUB_APP_CLIENT_SECRET,
92-
process.env.KMS_KEY_ID,
93-
process.env.ENVIRONMENT,
99+
expect(mockedGetParameter).toBeCalledWith(
100+
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME
101+
);
102+
expect(mockedGetParameter).toBeCalledWith(
103+
process.env.GITHUB_APP_ID_PARAMETER_NAME
104+
);
105+
expect(mockedGetParameter).toBeCalledWith(
106+
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME
94107
);
95-
expect(mockedDecrypt).toBeCalledWith(
96-
process.env.GITHUB_APP_KEY_BASE64,
97-
process.env.KMS_KEY_ID,
98-
process.env.ENVIRONMENT,
108+
expect(mockedGetParameter).toBeCalledWith(
109+
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME
99110
);
100111
expect(mockedCreatAppAuth).toBeCalledTimes(1);
101112
expect(mockedCreatAppAuth).toBeCalledWith(authOptions);
@@ -113,34 +124,43 @@ describe('Test createGithubAuth', () => {
113124
});
114125

115126
const authOptions = {
116-
appId: parseInt(process.env.GITHUB_APP_ID as string),
117-
privateKey: 'decryptedValue',
127+
appId: parseInt(appId),
128+
privateKey,
118129
installationId,
119-
clientId: process.env.GITHUB_APP_CLIENT_ID,
120-
clientSecret: 'decryptedValue',
130+
clientId,
131+
clientSecret,
121132
request: mockedRequestInterface.defaults({ baseUrl: githubServerUrl }),
122133
};
123134

124-
mockedDecrypt.mockResolvedValueOnce(decryptedValue).mockResolvedValueOnce(b64);
135+
const mockedGetParameter = jest.fn()
136+
.mockResolvedValueOnce(privateKeyBase64)
137+
.mockResolvedValueOnce(appId)
138+
.mockResolvedValueOnce(clientId)
139+
.mockResolvedValueOnce(clientSecret);
140+
mockedSSM.mockImplementation(() => ({
141+
ssm: null as any,
142+
getParameter: mockedGetParameter,
143+
}));
144+
125145
const mockedAuth = jest.fn();
126146
mockedAuth.mockResolvedValue({ token });
127-
mockedCreatAppAuth.mockImplementation((authOptions: StrategyOptions) => {
128-
return mockedAuth;
129-
});
147+
mockedCreatAppAuth.mockImplementation(() => mockedAuth);
130148

131149
// Act
132150
const result = await createGithubAuth(installationId, authType, githubServerUrl);
133151

134152
// Assert
135-
expect(mockedDecrypt).toBeCalledWith(
136-
process.env.GITHUB_APP_CLIENT_SECRET,
137-
process.env.KMS_KEY_ID,
138-
process.env.ENVIRONMENT,
153+
expect(mockedGetParameter).toBeCalledWith(
154+
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME
155+
);
156+
expect(mockedGetParameter).toBeCalledWith(
157+
process.env.GITHUB_APP_ID_PARAMETER_NAME
158+
);
159+
expect(mockedGetParameter).toBeCalledWith(
160+
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME
139161
);
140-
expect(mockedDecrypt).toBeCalledWith(
141-
process.env.GITHUB_APP_KEY_BASE64,
142-
process.env.KMS_KEY_ID,
143-
process.env.ENVIRONMENT,
162+
expect(mockedGetParameter).toBeCalledWith(
163+
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME
144164
);
145165
expect(mockedCreatAppAuth).toBeCalledTimes(1);
146166
expect(mockedCreatAppAuth).toBeCalledWith(authOptions);

modules/runners/lambdas/runners/src/scale-runners/gh-auth.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { request } from '@octokit/request';
33
import { createAppAuth } from '@octokit/auth-app';
44
import { Authentication, StrategyOptions } from '@octokit/auth-app/dist-types/types';
55
import { OctokitOptions } from '@octokit/core/dist-types/types';
6-
import { decrypt } from './kms';
6+
import SSM from './ssm';
77

88
export async function createOctoClient(token: string, ghesApiUrl = ''): Promise<Octokit> {
99
const ocktokitOptions: OctokitOptions = {
@@ -21,25 +21,28 @@ export async function createGithubAuth(
2121
authType: 'app' | 'installation',
2222
ghesApiUrl = '',
2323
): Promise<Authentication> {
24-
const clientSecret = await decrypt(
25-
process.env.GITHUB_APP_CLIENT_SECRET as string,
26-
process.env.KMS_KEY_ID as string,
27-
process.env.ENVIRONMENT as string,
24+
const ssm = new SSM();
25+
const privateKeyBase64 = await ssm.getParameter(
26+
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME as string
2827
);
29-
const privateKeyBase64 = await decrypt(
30-
process.env.GITHUB_APP_KEY_BASE64 as string,
31-
process.env.KMS_KEY_ID as string,
32-
process.env.ENVIRONMENT as string,
28+
const appIdString = await ssm.getParameter(
29+
process.env.GITHUB_APP_ID_PARAMETER_NAME as string
3330
);
34-
35-
if (clientSecret === undefined || privateKeyBase64 === undefined) {
36-
throw Error('Cannot decrypt.');
31+
const clientId = await ssm.getParameter(
32+
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME as string
33+
);
34+
const clientSecret = await ssm.getParameter(
35+
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME as string
36+
);
37+
if (privateKeyBase64 === undefined
38+
|| appIdString === undefined
39+
|| clientId === undefined
40+
|| clientSecret === undefined) {
41+
throw Error('Cannot decrypt GitHub App parameters.');
3742
}
3843

3944
const privateKey = Buffer.from(privateKeyBase64, 'base64').toString();
40-
41-
const appId: number = parseInt(process.env.GITHUB_APP_ID as string);
42-
const clientId = process.env.GITHUB_APP_CLIENT_ID as string;
45+
const appId = parseInt(appIdString);
4346

4447
const authOptions: StrategyOptions = {
4548
appId,

modules/runners/lambdas/runners/src/scale-runners/kms/index.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

modules/runners/lambdas/runners/src/scale-runners/scale-down.test.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ jest.mock('@octokit/rest', () => ({
2525

2626
jest.mock('./runners');
2727

28+
jest.mock('./ssm', () => jest.fn().mockImplementation(() => ({
29+
getParameter: jest.fn().mockImplementation(() => ''),
30+
})));
31+
2832
export interface TestData {
2933
repositoryName: string;
3034
repositoryOwner: string;
@@ -105,10 +109,10 @@ const DEFAULT_REGISTERED_RUNNERS = [
105109

106110
describe('scaleDown', () => {
107111
beforeEach(() => {
108-
process.env.GITHUB_APP_KEY_BASE64 = 'TEST_CERTIFICATE_DATA';
109-
process.env.GITHUB_APP_ID = '1337';
110-
process.env.GITHUB_APP_CLIENT_ID = 'TEST_CLIENT_ID';
111-
process.env.GITHUB_APP_CLIENT_SECRET = 'TEST_CLIENT_SECRET';
112+
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
113+
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
114+
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
115+
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
112116
process.env.RUNNERS_MAXIMUM_COUNT = '3';
113117
process.env.ENVIRONMENT = environment;
114118
process.env.MINIMUM_RUNNING_TIME_IN_MINUTES = minimumRunningTimeInMinutes.toString();
@@ -319,10 +323,10 @@ describe('scaleDown', () => {
319323

320324
describe('scaleDown ghes', () => {
321325
beforeEach(() => {
322-
process.env.GITHUB_APP_KEY_BASE64 = 'TEST_CERTIFICATE_DATA';
323-
process.env.GITHUB_APP_ID = '1337';
324-
process.env.GITHUB_APP_CLIENT_ID = 'TEST_CLIENT_ID';
325-
process.env.GITHUB_APP_CLIENT_SECRET = 'TEST_CLIENT_SECRET';
326+
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
327+
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
328+
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
329+
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
326330
process.env.RUNNERS_MAXIMUM_COUNT = '3';
327331
process.env.ENVIRONMENT = environment;
328332
process.env.MINIMUM_RUNNING_TIME_IN_MINUTES = minimumRunningTimeInMinutes.toString();

modules/runners/lambdas/runners/src/scale-runners/scale-up.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ jest.mock('@octokit/rest', () => ({
2424

2525
jest.mock('./runners');
2626

27+
jest.mock('./ssm', () => jest.fn().mockImplementation(() => ({
28+
getParameter: jest.fn().mockImplementation(() => ''),
29+
})));
30+
2731
const TEST_DATA: ActionRequestMessage = {
2832
id: 1,
2933
eventType: 'check_run',
@@ -47,10 +51,10 @@ beforeEach(() => {
4751
jest.resetModules();
4852
jest.clearAllMocks();
4953
process.env = { ...cleanEnv };
50-
process.env.GITHUB_APP_KEY_BASE64 = 'TEST_CERTIFICATE_DATA';
51-
process.env.GITHUB_APP_ID = '1337';
52-
process.env.GITHUB_APP_CLIENT_ID = 'TEST_CLIENT_ID';
53-
process.env.GITHUB_APP_CLIENT_SECRET = 'TEST_CLIENT_SECRET';
54+
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
55+
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
56+
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
57+
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
5458
process.env.RUNNERS_MAXIMUM_COUNT = '3';
5559
process.env.ENVIRONMENT = 'unit-test-environment';
5660

0 commit comments

Comments
 (0)