Merge pull request #35 from Sea-Haven-Industries/feature/migrate-secrets-to-secrets-manager

Migrate secrets to Secrets Manager + add samconfig.toml.example
This commit is contained in:
Adam Moussa 2026-06-02 20:34:05 -04:00 • committed by GitHub
commit 6794e4950a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 106 additions and 169 deletions

22
package-lock.json generated
View file

@ -13,7 +13,6 @@
"@aws-sdk/client-s3": "^3.1059.0",
"@aws-sdk/client-secrets-manager": "^3.1059.0",
"@aws-sdk/client-sqs": "^3.1059.0",
"@aws-sdk/client-ssm": "^3.1059.0",
"@aws-sdk/lib-dynamodb": "^3.1059.0",
"csv-parse": "^6.2.1",
"mailparser": "^3.9.9"
@ -337,27 +336,6 @@
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/client-ssm": {
"version": "3.1059.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.1059.0.tgz",
"integrity": "sha512-c6VaL8BKDeDSHWz59ELsYZcQit7r4aofVm07acWnxw1DQy5amQagOymPDhYwIpB6H3wZNMDu4FK3FxecNDzGww==",
"license": "Apache-2.0",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "^3.974.16",
"@aws-sdk/credential-provider-node": "^3.972.49",
"@aws-sdk/types": "^3.973.10",
"@smithy/core": "^3.24.6",
"@smithy/fetch-http-handler": "^5.4.6",
"@smithy/node-http-handler": "^4.7.6",
"@smithy/types": "^4.14.3",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/@aws-sdk/core": {
"version": "3.974.16",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.16.tgz",

View file

@ -12,7 +12,6 @@
"@aws-sdk/client-s3": "^3.1059.0",
"@aws-sdk/client-secrets-manager": "^3.1059.0",
"@aws-sdk/client-sqs": "^3.1059.0",
"@aws-sdk/client-ssm": "^3.1059.0",
"@aws-sdk/lib-dynamodb": "^3.1059.0",
"csv-parse": "^6.2.1",
"mailparser": "^3.9.9"

10
samconfig.toml.example Normal file
View file

@ -0,0 +1,10 @@
# Copy this file to samconfig.toml (gitignored) and adjust as needed for local deploys.
# CI/CD deploys via the reusable cd-sam.yaml workflow and does not use this file.
version = 0.1
[default.deploy.parameters]
stack_name = "payments-dashboard"
region = "us-east-1"
resolve_s3 = true
capabilities = "CAPABILITY_IAM"
confirm_changeset = true

View file

@ -1,16 +1,16 @@
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, ScanCommand } = require("@aws-sdk/lib-dynamodb");
const { SSMClient, GetParameterCommand } = require("@aws-sdk/client-ssm");
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({ region: "us-east-1" }));
const ssm = new SSMClient({ region: "us-east-1" });
const secrets = new SecretsManagerClient({ region: "us-east-1" });
const BOA_BASE_URL = "https://api.bofa.com";
async function getSSMParam(name) {
const { Parameter } = await ssm.send(
new GetParameterCommand({ Name: name, WithDecryption: true })
async function getSecretJson(secretId) {
const { SecretString } = await secrets.send(
new GetSecretValueCommand({ SecretId: secretId })
);
return Parameter.Value;
return JSON.parse(SecretString);
}
async function getAccessToken(applicationID, clientId, clientSecret) {
@ -115,13 +115,7 @@ async function getAccessToken(applicationID, clientId, clientSecret) {
}
// Get credentials
const [appId, clientId, clientSecret, accountNumber, companyId] = await Promise.all([
getSSMParam("/payments-dashboard/boa-check-mgmt-app-id"),
getSSMParam("/payments-dashboard/boa-check-mgmt-client-id"),
getSSMParam("/payments-dashboard/boa-check-mgmt-token"),
getSSMParam("/payments-dashboard/boa-account-number"),
getSSMParam("/payments-dashboard/boa-company-id"),
]);
const { appId, clientId, token: clientSecret, accountNumber, companyId } = await getSecretJson("payments-dashboard/boa-check-mgmt");
// Fill in account number
for (const item of issueList) {

View file

@ -8,16 +8,16 @@
* node scripts/test-boa-prod.js
*/
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
const ssm = new SSMClient();
const secrets = new SecretsManagerClient();
const BASE_URL = "https://api.bofa.com";
async function getSSMParam(name) {
const { Parameter } = await ssm.send(
new GetParameterCommand({ Name: name, WithDecryption: true })
async function getSecretJson(secretId) {
const { SecretString } = await secrets.send(
new GetSecretValueCommand({ SecretId: secretId })
);
return Parameter.Value;
return JSON.parse(SecretString);
}
async function getAccessToken(applicationID, clientId, clientSecret) {
@ -44,27 +44,19 @@ async function getAccessToken(applicationID, clientId, clientSecret) {
}
async function main() {
console.log("Loading credentials from SSM...\n");
console.log("Loading credentials from Secrets Manager...\n");
const [
checkMgmtAppId,
checkMgmtClientId,
checkMgmtSecret,
reportingAppId,
reportingClientId,
reportingSecret,
accountNumber,
bankId,
] = await Promise.all([
getSSMParam("/payments-dashboard/boa-check-mgmt-app-id"),
getSSMParam("/payments-dashboard/boa-check-mgmt-client-id"),
getSSMParam("/payments-dashboard/boa-check-mgmt-token"),
getSSMParam("/payments-dashboard/boa-reporting-app-id"),
getSSMParam("/payments-dashboard/boa-account-info-client-id"),
getSSMParam("/payments-dashboard/boa-account-info-token"),
getSSMParam("/payments-dashboard/boa-account-number"),
getSSMParam("/payments-dashboard/boa-bank-id"),
]);
const checkMgmt = await getSecretJson("payments-dashboard/boa-check-mgmt");
const reporting = await getSecretJson("payments-dashboard/boa-reporting");
const checkMgmtAppId = checkMgmt.appId;
const checkMgmtClientId = checkMgmt.clientId;
const checkMgmtSecret = checkMgmt.token;
const reportingAppId = reporting.appId;
const reportingClientId = reporting.clientId;
const reportingSecret = reporting.token;
const accountNumber = reporting.accountNumber;
const bankId = reporting.bankId;
console.log("Credentials loaded.\n");

View file

@ -11,17 +11,17 @@
* node scripts/test-boa-sandbox.js
*/
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
const ssm = new SSMClient();
const secrets = new SecretsManagerClient();
const SANDBOX_BASE = "https://api-sb.bofa.com";
const AUTH_URL = `${SANDBOX_BASE}/authn/v1/client-authentication`;
async function getSSMParam(name) {
const { Parameter } = await ssm.send(
new GetParameterCommand({ Name: name, WithDecryption: true })
async function getSecretJson(secretId) {
const { SecretString } = await secrets.send(
new GetSecretValueCommand({ SecretId: secretId })
);
return Parameter.Value;
return JSON.parse(SecretString);
}
async function getAccessToken(applicationID, clientId, clientSecret) {
@ -51,23 +51,17 @@ async function getAccessToken(applicationID, clientId, clientSecret) {
}
async function main() {
console.log("Loading credentials from SSM...\n");
console.log("Loading credentials from Secrets Manager...\n");
const [
checkMgmtClientId,
checkMgmtSecret,
accountInfoClientId,
accountInfoSecret,
accountNumber,
companyId,
] = await Promise.all([
getSSMParam("/payments-dashboard/boa-check-mgmt-client-id"),
getSSMParam("/payments-dashboard/boa-check-mgmt-token"),
getSSMParam("/payments-dashboard/boa-account-info-client-id"),
getSSMParam("/payments-dashboard/boa-account-info-token"),
getSSMParam("/payments-dashboard/boa-account-number"),
getSSMParam("/payments-dashboard/boa-company-id"),
]);
const checkMgmt = await getSecretJson("payments-dashboard/boa-check-mgmt");
const reporting = await getSecretJson("payments-dashboard/boa-reporting");
const checkMgmtClientId = checkMgmt.clientId;
const checkMgmtSecret = checkMgmt.token;
const accountInfoClientId = reporting.clientId;
const accountInfoSecret = reporting.token;
const accountNumber = checkMgmt.accountNumber;
const companyId = checkMgmt.companyId;
console.log("Credentials loaded.\n");

View file

@ -1,17 +1,20 @@
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, ScanCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb";
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient());
const ssm = new SSMClient();
const secrets = new SecretsManagerClient();
const TABLE_NAME = process.env.TABLE_NAME;
const BOA_BASE_URL = process.env.BOA_BASE_URL;
async function getSSMParam(name) {
const { Parameter } = await ssm.send(
new GetParameterCommand({ Name: name, WithDecryption: true })
let cachedCreds;
async function getReportingCreds() {
if (cachedCreds) return cachedCreds;
const { SecretString } = await secrets.send(
new GetSecretValueCommand({ SecretId: process.env.BOA_REPORTING_SECRET_NAME })
);
return Parameter.Value;
cachedCreds = JSON.parse(SecretString);
return cachedCreds;
}
async function getAccessToken(applicationID, clientId, clientSecret) {
@ -34,13 +37,7 @@ async function getAccessToken(applicationID, clientId, clientSecret) {
}
export const handler = async () => {
const [appId, clientId, clientSecret, accountNumber, bankId] = await Promise.all([
getSSMParam(process.env.BOA_REPORTING_APP_ID_PARAM),
getSSMParam(process.env.BOA_REPORTING_CLIENT_ID_PARAM),
getSSMParam(process.env.BOA_REPORTING_SECRET_PARAM),
getSSMParam(process.env.BOA_ACCOUNT_NUMBER_PARAM),
getSSMParam(process.env.BOA_BANK_ID_PARAM),
]);
const { appId, clientId, token: clientSecret, accountNumber, bankId } = await getReportingCreds();
const bearerToken = await getAccessToken(appId, clientId, clientSecret);

View file

@ -1,20 +1,23 @@
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, GetCommand, PutCommand, UpdateCommand, ScanCommand } from "@aws-sdk/lib-dynamodb";
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
import { parse } from "csv-parse/sync";
const s3 = new S3Client();
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient());
const ssm = new SSMClient();
const secrets = new SecretsManagerClient();
const TABLE_NAME = process.env.TABLE_NAME;
const BOA_BASE_URL = process.env.BOA_BASE_URL;
async function getSSMParam(name) {
const { Parameter } = await ssm.send(
new GetParameterCommand({ Name: name, WithDecryption: true })
let cachedCreds;
async function getCheckMgmtCreds() {
if (cachedCreds) return cachedCreds;
const { SecretString } = await secrets.send(
new GetSecretValueCommand({ SecretId: process.env.BOA_CHECK_MGMT_SECRET_NAME })
);
return Parameter.Value;
cachedCreds = JSON.parse(SecretString);
return cachedCreds;
}
async function getAccessToken(applicationID, clientId, clientSecret) {
@ -97,13 +100,7 @@ async function backfillBoA() {
console.log(`Backfill: ${toSubmit.length} checks to submit`);
const [appId, clientId, clientSecret, accountNumber, companyId] = await Promise.all([
getSSMParam(process.env.BOA_CHECK_MGMT_APP_ID_PARAM),
getSSMParam(process.env.BOA_CHECK_MGMT_CLIENT_ID_PARAM),
getSSMParam(process.env.BOA_CHECK_MGMT_SECRET_PARAM),
getSSMParam(process.env.BOA_ACCOUNT_NUMBER_PARAM),
getSSMParam(process.env.BOA_COMPANY_ID_PARAM),
]);
const { appId, clientId, token: clientSecret, accountNumber, companyId } = await getCheckMgmtCreds();
const bearerToken = await getAccessToken(appId, clientId, clientSecret);
const BOA_BATCH_SIZE = 100;
@ -379,13 +376,7 @@ export const handler = async (event) => {
// Submit to CashPro if there are any new issues or cancels
if (newChecks.length || cancelChecks.length) {
const [appId, clientId, clientSecret, accountNumber, companyId] = await Promise.all([
getSSMParam(process.env.BOA_CHECK_MGMT_APP_ID_PARAM),
getSSMParam(process.env.BOA_CHECK_MGMT_CLIENT_ID_PARAM),
getSSMParam(process.env.BOA_CHECK_MGMT_SECRET_PARAM),
getSSMParam(process.env.BOA_ACCOUNT_NUMBER_PARAM),
getSSMParam(process.env.BOA_COMPANY_ID_PARAM),
]);
const { appId, clientId, token: clientSecret, accountNumber, companyId } = await getCheckMgmtCreds();
const bearerToken = await getAccessToken(appId, clientId, clientSecret);

View file

@ -8,13 +8,13 @@ import {
BatchWriteCommand,
} from "@aws-sdk/lib-dynamodb";
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
import { simpleParser } from "mailparser";
const s3 = new S3Client();
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient());
const sqs = new SQSClient();
const ssm = new SSMClient();
const secrets = new SecretsManagerClient();
const TABLE_NAME = process.env.TABLE_NAME;
const CHANNEL_ID = process.env.PAYROLL_CHANNEL_ID;
@ -23,13 +23,10 @@ const QUEUE_URL = process.env.PAYROLL_BATCH_QUEUE_URL;
let cachedToken;
async function getSlackToken() {
if (cachedToken) return cachedToken;
const { Parameter } = await ssm.send(
new GetParameterCommand({
Name: process.env.SLACK_BOT_TOKEN_PARAM,
WithDecryption: true,
})
const { SecretString } = await secrets.send(
new GetSecretValueCommand({ SecretId: process.env.SLACK_BOT_TOKEN_SECRET_NAME })
);
cachedToken = Parameter.Value;
cachedToken = SecretString;
return cachedToken;
}

View file

@ -1,21 +1,18 @@
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, GetCommand, ScanCommand } from "@aws-sdk/lib-dynamodb";
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient());
const ssm = new SSMClient();
const secrets = new SecretsManagerClient();
const TABLE_NAME = process.env.TABLE_NAME;
let cachedToken;
async function getSlackToken() {
if (cachedToken) return cachedToken;
const { Parameter } = await ssm.send(
new GetParameterCommand({
Name: process.env.SLACK_BOT_TOKEN_PARAM,
WithDecryption: true,
})
const { SecretString } = await secrets.send(
new GetSecretValueCommand({ SecretId: process.env.SLACK_BOT_TOKEN_SECRET_NAME })
);
cachedToken = Parameter.Value;
cachedToken = SecretString;
return cachedToken;
}

View file

@ -241,7 +241,7 @@ Resources:
Timeout: 60
Environment:
Variables:
SLACK_BOT_TOKEN_PARAM: /payments-dashboard/slack-bot-token
SLACK_BOT_TOKEN_SECRET_NAME: payments-dashboard/slack-bot-token
PAYROLL_CHANNEL_ID: C0AV5RBMYKU
PAYROLL_BATCH_QUEUE_URL: !Ref PayrollBatchQueue
Events:
@ -265,8 +265,11 @@ Resources:
BucketName: !Sub seahaven-payroll-emails-${AWS::AccountId}
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/slack-bot-token
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:payments-dashboard/slack-bot-token-*
- SQSSendMessagePolicy:
QueueName: !GetAtt PayrollBatchQueue.QueueName
- SQSPollerPolicy:
@ -281,11 +284,7 @@ Resources:
Environment:
Variables:
BOA_BASE_URL: https://api.bofa.com
BOA_CHECK_MGMT_APP_ID_PARAM: /payments-dashboard/boa-check-mgmt-app-id
BOA_CHECK_MGMT_CLIENT_ID_PARAM: /payments-dashboard/boa-check-mgmt-client-id
BOA_CHECK_MGMT_SECRET_PARAM: /payments-dashboard/boa-check-mgmt-token
BOA_ACCOUNT_NUMBER_PARAM: /payments-dashboard/boa-account-number
BOA_COMPANY_ID_PARAM: /payments-dashboard/boa-company-id
BOA_CHECK_MGMT_SECRET_NAME: payments-dashboard/boa-check-mgmt
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet
@ -307,16 +306,11 @@ Resources:
BucketName: !Sub seahaven-payments-csv-${AWS::AccountId}
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-app-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-client-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-token
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-number
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-company-id
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:payments-dashboard/boa-check-mgmt-*
- Version: "2012-10-17"
Statement:
- Effect: Allow
@ -338,7 +332,7 @@ Resources:
- !Ref LambdaSecurityGroup
Environment:
Variables:
SLACK_BOT_TOKEN_PARAM: /payments-dashboard/slack-bot-token
SLACK_BOT_TOKEN_SECRET_NAME: payments-dashboard/slack-bot-token
Events:
SlackEvent:
Type: HttpApi
@ -348,8 +342,11 @@ Resources:
Policies:
- DynamoDBReadPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/slack-bot-token
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:payments-dashboard/slack-bot-token-*
- Version: "2012-10-17"
Statement:
- Effect: Allow
@ -373,11 +370,7 @@ Resources:
Environment:
Variables:
BOA_BASE_URL: https://api.bofa.com
BOA_REPORTING_APP_ID_PARAM: /payments-dashboard/boa-reporting-app-id
BOA_REPORTING_CLIENT_ID_PARAM: /payments-dashboard/boa-account-info-client-id
BOA_REPORTING_SECRET_PARAM: /payments-dashboard/boa-account-info-token
BOA_ACCOUNT_NUMBER_PARAM: /payments-dashboard/boa-account-number
BOA_BANK_ID_PARAM: /payments-dashboard/boa-bank-id
BOA_REPORTING_SECRET_NAME: payments-dashboard/boa-reporting
Events:
DailySchedule:
Type: Schedule
@ -388,16 +381,11 @@ Resources:
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-reporting-app-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-info-client-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-info-token
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-number
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-bank-id
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:payments-dashboard/boa-reporting-*
- Version: "2012-10-17"
Statement:
- Effect: Allow