Skip to content

Commit

Permalink
Merge pull request #2 from NarrativeApp/feat/ssm-parameter-store
Browse files Browse the repository at this point in the history
feat: use SSM Parameter Store to store secrets
  • Loading branch information
Stijn Seghers authored Apr 28, 2021
2 parents 797fb7d + 6dff0e9 commit fa719d6
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 180 deletions.
24 changes: 0 additions & 24 deletions modules/runners/encrypt.tf

This file was deleted.

16 changes: 8 additions & 8 deletions modules/runners/lambdas/runners/src/lambda.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { scaleUp } from './scale-runners/scale-up';
import { scaleDown } from './scale-runners/scale-down';
import { scaleUp as scaleUp_ } from './scale-runners/scale-up';
import { scaleDown as scaleDown_ } from './scale-runners/scale-down';
import { SQSEvent, ScheduledEvent, Context } from 'aws-lambda';

module.exports.scaleUp = async (event: SQSEvent, context: Context, callback: any) => {
export async function scaleUp(event: SQSEvent, context: Context, callback: any) {
console.dir(event, { depth: 5 });
try {
for (const e of event.Records) {
await scaleUp(e.eventSource, JSON.parse(e.body));
await scaleUp_(e.eventSource, JSON.parse(e.body));
}
return callback(null);
} catch (e) {
console.error(e);
return callback('Failed handling SQS event');
}
};
}

module.exports.scaleDown = async (event: ScheduledEvent, context: Context, callback: any) => {
export async function scaleDown(event: ScheduledEvent, context: Context, callback: any) {
try {
scaleDown();
scaleDown_();
return callback(null);
} catch (e) {
console.error(e);
return callback('Failed');
}
};
}
106 changes: 63 additions & 43 deletions modules/runners/lambdas/runners/src/scale-runners/gh-auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { createOctoClient, createGithubAuth } from './gh-auth';
import nock from 'nock';
import { createAppAuth } from '@octokit/auth-app';
import { StrategyOptions } from '@octokit/auth-app/dist-types/types';
import { decrypt } from './kms';
import SSM from './ssm';
import { RequestInterface } from '@octokit/types';
import { mock, MockProxy } from 'jest-mock-extended';
import { request } from '@octokit/request';

jest.mock('./kms');
jest.mock('@octokit/auth-app');
jest.mock('./ssm');

const cleanEnv = process.env;

Expand All @@ -19,7 +19,7 @@ beforeEach(() => {
nock.disableNetConnect();
});

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

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

const installationId = 1;
const authType = 'app';
const token = '123456';
const decryptedValue = 'decryptedValue';
const b64 = Buffer.from(decryptedValue, 'binary').toString('base64');
const privateKey = 'my-private-key';
const privateKeyBase64 = Buffer.from(privateKey, 'binary').toString('base64');
const appId = '123';
const clientId = 'abc';
const clientSecret = 'abcdef123456';

beforeEach(() => {
process.env.GITHUB_APP_ID = '1';
process.env.GITHUB_APP_CLIENT_SECRET = 'client_secret';
process.env.GITHUB_APP_KEY_BASE64 = 'base64';
process.env.KMS_KEY_ID = 'key_id';
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
process.env.ENVIRONMENT = 'dev';
process.env.GITHUB_APP_CLIENT_ID = '1';
});

test('Creates auth object for public GitHub', async () => {
// Arrange
const authOptions = {
appId: parseInt(process.env.GITHUB_APP_ID as string),
privateKey: 'decryptedValue',
appId: parseInt(appId),
privateKey,
installationId,
clientId: process.env.GITHUB_APP_CLIENT_ID,
clientSecret: 'decryptedValue',
clientId,
clientSecret,
};

mockedDecrypt.mockResolvedValueOnce(decryptedValue).mockResolvedValueOnce(b64);
const mockedGetParameter = jest.fn()
.mockResolvedValueOnce(privateKeyBase64)
.mockResolvedValueOnce(appId)
.mockResolvedValueOnce(clientId)
.mockResolvedValueOnce(clientSecret);
mockedSSM.mockImplementation(() => ({
ssm: null as any,
getParameter: mockedGetParameter,
}));

const mockedAuth = jest.fn();
mockedAuth.mockResolvedValue({ token });
mockedCreatAppAuth.mockImplementation((authOptions: StrategyOptions) => {
return mockedAuth;
});
mockedCreatAppAuth.mockImplementation(() => mockedAuth);

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

// Assert
expect(mockedDecrypt).toBeCalledWith(
process.env.GITHUB_APP_CLIENT_SECRET,
process.env.KMS_KEY_ID,
process.env.ENVIRONMENT,
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME
);
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_ID_PARAMETER_NAME
);
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME
);
expect(mockedDecrypt).toBeCalledWith(
process.env.GITHUB_APP_KEY_BASE64,
process.env.KMS_KEY_ID,
process.env.ENVIRONMENT,
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME
);
expect(mockedCreatAppAuth).toBeCalledTimes(1);
expect(mockedCreatAppAuth).toBeCalledWith(authOptions);
Expand All @@ -113,34 +124,43 @@ describe('Test createGithubAuth', () => {
});

const authOptions = {
appId: parseInt(process.env.GITHUB_APP_ID as string),
privateKey: 'decryptedValue',
appId: parseInt(appId),
privateKey,
installationId,
clientId: process.env.GITHUB_APP_CLIENT_ID,
clientSecret: 'decryptedValue',
clientId,
clientSecret,
request: mockedRequestInterface.defaults({ baseUrl: githubServerUrl }),
};

mockedDecrypt.mockResolvedValueOnce(decryptedValue).mockResolvedValueOnce(b64);
const mockedGetParameter = jest.fn()
.mockResolvedValueOnce(privateKeyBase64)
.mockResolvedValueOnce(appId)
.mockResolvedValueOnce(clientId)
.mockResolvedValueOnce(clientSecret);
mockedSSM.mockImplementation(() => ({
ssm: null as any,
getParameter: mockedGetParameter,
}));

const mockedAuth = jest.fn();
mockedAuth.mockResolvedValue({ token });
mockedCreatAppAuth.mockImplementation((authOptions: StrategyOptions) => {
return mockedAuth;
});
mockedCreatAppAuth.mockImplementation(() => mockedAuth);

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

// Assert
expect(mockedDecrypt).toBeCalledWith(
process.env.GITHUB_APP_CLIENT_SECRET,
process.env.KMS_KEY_ID,
process.env.ENVIRONMENT,
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME
);
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_ID_PARAMETER_NAME
);
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME
);
expect(mockedDecrypt).toBeCalledWith(
process.env.GITHUB_APP_KEY_BASE64,
process.env.KMS_KEY_ID,
process.env.ENVIRONMENT,
expect(mockedGetParameter).toBeCalledWith(
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME
);
expect(mockedCreatAppAuth).toBeCalledTimes(1);
expect(mockedCreatAppAuth).toBeCalledWith(authOptions);
Expand Down
33 changes: 18 additions & 15 deletions modules/runners/lambdas/runners/src/scale-runners/gh-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { request } from '@octokit/request';
import { createAppAuth } from '@octokit/auth-app';
import { Authentication, StrategyOptions } from '@octokit/auth-app/dist-types/types';
import { OctokitOptions } from '@octokit/core/dist-types/types';
import { decrypt } from './kms';
import SSM from './ssm';

export async function createOctoClient(token: string, ghesApiUrl = ''): Promise<Octokit> {
const ocktokitOptions: OctokitOptions = {
Expand All @@ -21,25 +21,28 @@ export async function createGithubAuth(
authType: 'app' | 'installation',
ghesApiUrl = '',
): Promise<Authentication> {
const clientSecret = await decrypt(
process.env.GITHUB_APP_CLIENT_SECRET as string,
process.env.KMS_KEY_ID as string,
process.env.ENVIRONMENT as string,
const ssm = new SSM();
const privateKeyBase64 = await ssm.getParameter(
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME as string
);
const privateKeyBase64 = await decrypt(
process.env.GITHUB_APP_KEY_BASE64 as string,
process.env.KMS_KEY_ID as string,
process.env.ENVIRONMENT as string,
const appIdString = await ssm.getParameter(
process.env.GITHUB_APP_ID_PARAMETER_NAME as string
);

if (clientSecret === undefined || privateKeyBase64 === undefined) {
throw Error('Cannot decrypt.');
const clientId = await ssm.getParameter(
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME as string
);
const clientSecret = await ssm.getParameter(
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME as string
);
if (privateKeyBase64 === undefined
|| appIdString === undefined
|| clientId === undefined
|| clientSecret === undefined) {
throw Error('Cannot decrypt GitHub App parameters.');
}

const privateKey = Buffer.from(privateKeyBase64, 'base64').toString();

const appId: number = parseInt(process.env.GITHUB_APP_ID as string);
const clientId = process.env.GITHUB_APP_CLIENT_ID as string;
const appId = parseInt(appIdString);

const authOptions: StrategyOptions = {
appId,
Expand Down
25 changes: 0 additions & 25 deletions modules/runners/lambdas/runners/src/scale-runners/kms/index.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ jest.mock('@octokit/rest', () => ({

jest.mock('./runners');

jest.mock('./ssm', () => jest.fn().mockImplementation(() => ({
getParameter: jest.fn().mockImplementation(() => ''),
})));

export interface TestData {
repositoryName: string;
repositoryOwner: string;
Expand Down Expand Up @@ -105,10 +109,10 @@ const DEFAULT_REGISTERED_RUNNERS = [

describe('scaleDown', () => {
beforeEach(() => {
process.env.GITHUB_APP_KEY_BASE64 = 'TEST_CERTIFICATE_DATA';
process.env.GITHUB_APP_ID = '1337';
process.env.GITHUB_APP_CLIENT_ID = 'TEST_CLIENT_ID';
process.env.GITHUB_APP_CLIENT_SECRET = 'TEST_CLIENT_SECRET';
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
process.env.RUNNERS_MAXIMUM_COUNT = '3';
process.env.ENVIRONMENT = environment;
process.env.MINIMUM_RUNNING_TIME_IN_MINUTES = minimumRunningTimeInMinutes.toString();
Expand Down Expand Up @@ -319,10 +323,10 @@ describe('scaleDown', () => {

describe('scaleDown ghes', () => {
beforeEach(() => {
process.env.GITHUB_APP_KEY_BASE64 = 'TEST_CERTIFICATE_DATA';
process.env.GITHUB_APP_ID = '1337';
process.env.GITHUB_APP_CLIENT_ID = 'TEST_CLIENT_ID';
process.env.GITHUB_APP_CLIENT_SECRET = 'TEST_CLIENT_SECRET';
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
process.env.RUNNERS_MAXIMUM_COUNT = '3';
process.env.ENVIRONMENT = environment;
process.env.MINIMUM_RUNNING_TIME_IN_MINUTES = minimumRunningTimeInMinutes.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ jest.mock('@octokit/rest', () => ({

jest.mock('./runners');

jest.mock('./ssm', () => jest.fn().mockImplementation(() => ({
getParameter: jest.fn().mockImplementation(() => ''),
})));

const TEST_DATA: ActionRequestMessage = {
id: 1,
eventType: 'check_run',
Expand All @@ -47,10 +51,10 @@ beforeEach(() => {
jest.resetModules();
jest.clearAllMocks();
process.env = { ...cleanEnv };
process.env.GITHUB_APP_KEY_BASE64 = 'TEST_CERTIFICATE_DATA';
process.env.GITHUB_APP_ID = '1337';
process.env.GITHUB_APP_CLIENT_ID = 'TEST_CLIENT_ID';
process.env.GITHUB_APP_CLIENT_SECRET = 'TEST_CLIENT_SECRET';
process.env.GITHUB_APP_KEY_BASE64_PARAMETER_NAME = 'private-key';
process.env.GITHUB_APP_ID_PARAMETER_NAME = 'app-id';
process.env.GITHUB_APP_CLIENT_ID_PARAMETER_NAME = 'client-id';
process.env.GITHUB_APP_CLIENT_SECRET_PARAMETER_NAME = 'client-secret';
process.env.RUNNERS_MAXIMUM_COUNT = '3';
process.env.ENVIRONMENT = 'unit-test-environment';

Expand Down
Loading

0 comments on commit fa719d6

Please sign in to comment.