From eb4258d02d7657109a206e9c776d120fb105a6a1 Mon Sep 17 00:00:00 2001 From: Douglas Naphas Date: Fri, 1 Mar 2024 10:06:42 -0500 Subject: [PATCH] Get client secrets via dynamic refs To keep client secrets out of the synthesized templates. --- infra/bin/madliberation.ts | 12 ++++++++-- infra/lib/configureSocialIDPs.ts | 39 +++++++++++++++++++++++++++---- infra/lib/madliberation-webapp.ts | 6 ++--- infra/package.json | 1 + infra/scripts/synth-webapp.sh | 5 ++++ 5 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 infra/scripts/synth-webapp.sh diff --git a/infra/bin/madliberation.ts b/infra/bin/madliberation.ts index 94db8265..bacd1861 100644 --- a/infra/bin/madliberation.ts +++ b/infra/bin/madliberation.ts @@ -58,7 +58,7 @@ import { GitHubOidcRoleStacks } from "./GitHubOIDCRoleStacks"; const ssmParameterData: any = {}; let valueHash; getParametersResponse?.Parameters?.forEach( - (p: { Name: string; Value: string }) => { + (p: { Name: string; Value: string, Type: string }) => { console.log("(v3) Received parameter named:"); console.log(p.Name); const SHORT_PREFIX_LENGTH = 6; @@ -71,7 +71,15 @@ import { GitHubOidcRoleStacks } from "./GitHubOIDCRoleStacks"; console.log("(v3) value hash:"); console.log(valueHash); console.log("**************"); - ssmParameterData[p.Name] = p.Value; + if ( + p.Type === "SecureString" + ) { + // We'll access it via dynamic reference, to keep the secret value out + // of the template. + ssmParameterData[p.Name] = { name: p.Name, SecureString: true }; + } else { + ssmParameterData[p.Name] = p.Value; + } } ); console.log("=================="); diff --git a/infra/lib/configureSocialIDPs.ts b/infra/lib/configureSocialIDPs.ts index 40e3e582..a3beb656 100644 --- a/infra/lib/configureSocialIDPs.ts +++ b/infra/lib/configureSocialIDPs.ts @@ -1,6 +1,7 @@ -import { Stack } from "aws-cdk-lib"; +import { CfnDynamicReference, CfnDynamicReferenceService, SecretValue, Stack } from "aws-cdk-lib"; import { MadLiberationWebappProps } from "./madliberation-webapp"; import { aws_cognito as cognito } from "aws-cdk-lib"; +import { UserPoolIdentityProviderGoogle } from "aws-cdk-lib/aws-cognito"; const configureSocialIDPs: ( stack: Stack, props: MadLiberationWebappProps, @@ -21,7 +22,7 @@ const configureSocialIDPs: ( const userPoolIdentityProviderFacebook = new cognito.UserPoolIdentityProviderFacebook(stack, "Facebook", { clientId: facebookAppId, - clientSecret: facebookAppSecret, + clientSecret: "", // update via escape hatch userPool, scopes: ["public_profile", "email"], /* @@ -38,26 +39,46 @@ const configureSocialIDPs: ( email: cognito.ProviderAttribute.FACEBOOK_EMAIL, }, }); + const cfnUserPoolIdentityProviderFacebook = + userPoolIdentityProviderFacebook.node.defaultChild as + cognito.CfnUserPoolIdentityProvider; + cfnUserPoolIdentityProviderFacebook.addPropertyOverride( + "ProviderDetails.client_secret", + new CfnDynamicReference( + CfnDynamicReferenceService.SSM_SECURE, + facebookAppSecret.name + ) + ); userPool.registerIdentityProvider(userPoolIdentityProviderFacebook); } if (amazonClientId && amazonClientSecret) { const userPoolIdentityProviderAmazon = new cognito.UserPoolIdentityProviderAmazon(stack, "Amazon", { clientId: amazonClientId, - clientSecret: amazonClientSecret, + clientSecret: "", // update via escape hatch userPool, attributeMapping: { nickname: cognito.ProviderAttribute.AMAZON_NAME, email: cognito.ProviderAttribute.AMAZON_EMAIL, }, }); + const cfnUserPoolIdentityProviderAmazon = + userPoolIdentityProviderAmazon.node.defaultChild as + cognito.CfnUserPoolIdentityProvider; + cfnUserPoolIdentityProviderAmazon.addPropertyOverride( + "ProviderDetails.client_secret", + new CfnDynamicReference( + CfnDynamicReferenceService.SSM_SECURE, + amazonClientSecret.name + ) + ); userPool.registerIdentityProvider(userPoolIdentityProviderAmazon); } if (googleClientId && googleClientSecret) { const userPoolIdentityProviderGoogle = new cognito.UserPoolIdentityProviderGoogle(stack, "Google", { clientId: googleClientId, - clientSecret: googleClientSecret, + clientSecretValue: new SecretValue(""), userPool, scopes: ["profile", "email"], attributeMapping: { @@ -65,6 +86,16 @@ const configureSocialIDPs: ( email: cognito.ProviderAttribute.GOOGLE_EMAIL, }, }); + const cfnUserPoolIdentityProviderGoogle = + userPoolIdentityProviderGoogle.node.defaultChild as + cognito.CfnUserPoolIdentityProvider; + cfnUserPoolIdentityProviderGoogle.addPropertyOverride( + "ProviderDetails.client_secret", + new CfnDynamicReference( + CfnDynamicReferenceService.SSM_SECURE, + googleClientSecret.name + ) + ); userPool.registerIdentityProvider(userPoolIdentityProviderGoogle); } }; diff --git a/infra/lib/madliberation-webapp.ts b/infra/lib/madliberation-webapp.ts index 6b9df6fd..0165c76f 100644 --- a/infra/lib/madliberation-webapp.ts +++ b/infra/lib/madliberation-webapp.ts @@ -35,11 +35,11 @@ export interface MadLiberationWebappProps extends StackProps { domainName?: string; zoneId?: string; facebookAppId?: string; - facebookAppSecret?: string; + facebookAppSecret?: { name: string, SecureString: boolean }; amazonClientId?: string; - amazonClientSecret?: string; + amazonClientSecret?: { name: string, SecureString: boolean }; googleClientId?: string; - googleClientSecret?: string; + googleClientSecret?: { name: string, SecureString: boolean }; } export class MadliberationWebapp extends Stack { diff --git a/infra/package.json b/infra/package.json index c97b4798..aa9d086e 100644 --- a/infra/package.json +++ b/infra/package.json @@ -13,6 +13,7 @@ "synth": "npx cdk synth --no-staging", "diff": "npx cdk diff", "deploy-webapp": "bash scripts/deploy-webapp.sh", + "synth-webapp": "bash scripts/synth-webapp.sh", "itest": "bash scripts/itest.sh", "smoke": "bash scripts/smoke.sh" }, diff --git a/infra/scripts/synth-webapp.sh b/infra/scripts/synth-webapp.sh new file mode 100644 index 00000000..dc9c0f7c --- /dev/null +++ b/infra/scripts/synth-webapp.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -e +STACKNAME=$(npx @cdk-turnkey/stackname@1.2.0 --suffix webapp); +npx cdk synth ${STACKNAME};