Skip to content
This repository has been archived by the owner on Jan 2, 2024. It is now read-only.

Commit

Permalink
task: setup kms foundation
Browse files Browse the repository at this point in the history
  • Loading branch information
theworkflow committed Aug 18, 2023
1 parent a5d751c commit 3776b05
Show file tree
Hide file tree
Showing 8 changed files with 2,066 additions and 28 deletions.
22 changes: 22 additions & 0 deletions .repo/scripts/init-aws-resources.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

set -euo pipefail

LOCALSTACK_HOST=localhost
AWS_REGION=us-east-1

echo "Configuring KMS"
echo "==========================="
echo "######### Creating kms key and alias ###########"
kmsKey=$(awslocal kms create-key --description "local kms key" --key-usage ENCRYPT_DECRYPT)
keyId=$(echo ${kmsKey} | python3 -c "import sys, json; print(json.load(sys.stdin)['KeyMetadata']['KeyId'])")

echo "Configuring Secrets Manager"
echo "==========================="

# aws local is a dependency
echo "######### Creating secrets ###########"
awslocal secretsmanager create-secret \
--name local-api \
--description "Default secrets" \
--secret-string "{\"DEFAULT_SECRET\":\"I love dogs\", \"API_KMS_KEY_ID\": \"${keyId}\"}"
1,979 changes: 1,959 additions & 20 deletions api/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@aws-sdk/client-secrets-manager": "3.391.0",
"@hapi/hapi": "21.1.0",
"@hapi/inert": "7.0.0",
"@hapi/vision": "7.0.0",
Expand Down
9 changes: 6 additions & 3 deletions api/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,23 @@ interface Config {
MONGO_USER: string;
MONGO_PASSWORD: string;
SERVER_UID: string;
API_KMS_KEY_ID: string;
}

const serverUid = uuidv4();

export const baseConfig: Config = {
DEMO_ENVIRONMENT: process.env.THE_DEMO_ENVIRONMENT,
DEMO_ENVIRONMENT: process.env.DEMO_ENVIRONMENT,
APP_TOKEN: 'defaulttoken',
MONGO_URI: 'mongodb://mongo:mongo@mongo:27017',
MONGO_USER: 'mongo',
MONGO_PASSWORD: 'mongo',
SERVER_UID: serverUid,
API_KMS_KEY_ID: 'alias/local-api'
};

export const loadConfig = (logger: Logger): Promise<Config> => {
export const loadConfig = (logger: Logger, skipSecretsManager: boolean): Promise<Config> => {
const secretKey = `${baseConfig.DEMO_ENVIRONMENT}-api`;
const configLoader = new ConfigLoader<Config>(logger);
return configLoader.load(baseConfig);
return configLoader.load(baseConfig, secretKey, skipSecretsManager);
};
29 changes: 26 additions & 3 deletions api/src/lib/config-loader.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { Logger } from './logger';
import { SecretsManager } from './secrets-manager';

const isInEnvironment = (configKey: string) => process.env[configKey] && process.env[configKey] !== '';

const isInSecrets = (configKey: string, secretConfig: any) => secretConfig[configKey]
&& secretConfig[configKey] !== '';

const mask = (val: string) => '*'.repeat(val.length * 2);

export class ConfigLoader<T> {
constructor(private logger: Logger) {}
private secretsManager: SecretsManager;

constructor(private logger: Logger) {
this.secretsManager = new SecretsManager();
}

private mergeConfigs(config: any, secretConfig: any) {
return Object.keys(config).reduce((acc, key) => {
Expand All @@ -25,6 +33,15 @@ export class ConfigLoader<T> {
return acc;
}

if (isInSecrets(key, secretConfig)) {
const value = secretConfig[key];

this.logger.info(`Using secrets for ${key} ${mask(value)}`);
acc[key] = value;
return acc;
}


const value = config[key];
this.logger.info(`Using default config for ${key} ${mask(value)}`);
acc[key] = value;
Expand All @@ -36,7 +53,13 @@ export class ConfigLoader<T> {
}, {}) as T;
}

async load(config: T) {
return this.mergeConfigs(config, {});
async load(config: T, secretId: string, skipSecretsManager: boolean) {
if (skipSecretsManager) {
this.logger.info('Skipping secret loader');
return { ...config };
}

const secrets = await this.secretsManager.getSecret(secretId);
return this.mergeConfigs(config, secrets);
}
}
29 changes: 29 additions & 0 deletions api/src/lib/secrets-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
SecretsManagerClient,
SecretsManagerClientConfig,
GetSecretValueCommand,
GetSecretValueRequest
} from '@aws-sdk/client-secrets-manager';

export class SecretsManager {
private static NON_PROD_ENVS = ['local', 'test'];

private client: SecretsManagerClient;

constructor() {
const config: SecretsManagerClientConfig = {};

if (SecretsManager.NON_PROD_ENVS.includes(process.env.DEMO_ENVIRONMENT)) {
config.endpoint = 'http://localstack:4566';
}

this.client = new SecretsManagerClient(config);
}

async getSecret(secretId: string) {
const input: GetSecretValueRequest = { SecretId: secretId };
const command = new GetSecretValueCommand(input);
const { SecretString } = await this.client.send(command);
return JSON.parse(SecretString);
}
}
7 changes: 5 additions & 2 deletions api/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import { Logger, LogLevel } from './lib/logger';
import { createMongoConnection } from './lib/mongo';
import pkg from '../package.json';

export const runner = async (logLevel: LogLevel = 'info') => {
export const runner = async (
skipAwsSecrets: boolean = false,
logLevel: LogLevel = 'info'
) => {
const logger = new Logger({
name: `${pkg.name}-${baseConfig.SERVER_UID}`,
stream: process.stdout,
Expand All @@ -15,7 +18,7 @@ export const runner = async (logLevel: LogLevel = 'info') => {

try {
// setup configuration
const config = await loadConfig(logger);
const config = await loadConfig(logger, skipAwsSecrets);

// setup mongo
await createMongoConnection(config);
Expand Down
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ services:
- fintech-devcon-demo
volumes:
- ./.repo/data/mongo:/data/db
localstack:
image: localstack/localstack:0.14
container_name: localstack
hostname: localstack
environment:
- AWS_DEFAULT_REGION=us-east-1
- SERVICES=kms,secretsmanager
- API_ENVIRONMENT=local
ports:
- 4566:4566
networks:
- fintech-devcon-demo
volumes:
- "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
- "./.repo/scripts/init-aws-resources.sh:/docker-entrypoint-initaws.d/init-aws-resources.sh"

api:
image: harlanj/fintech-devcon-demo-api
container_name: fintech-devcon-demo-api
Expand Down Expand Up @@ -40,6 +57,7 @@ services:
- fintech-devcon-demo
depends_on:
- mongo
- localstack
frontend:
image: harlanj/fintech-devcon-demo-frontend
container_name: fintech-devcon-demo-frontend
Expand Down

0 comments on commit 3776b05

Please sign in to comment.