Skip to content

Commit

Permalink
Merge pull request #1270 from aligent/feature/DO-1394_use_SSM_for_API…
Browse files Browse the repository at this point in the history
…_key

Feature/do 1394 use ssm for api key
  • Loading branch information
crispy101 authored Dec 18, 2023
2 parents a73aa3a + 35b84b7 commit b7b423d
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 50 deletions.
26 changes: 19 additions & 7 deletions packages/prerender-fargate/lib/prerender-fargate-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,6 @@ export interface PrerenderFargateOptions {
* @default - 10 days
*/
expirationDays?: number;
/**
* A list of tokens to use for authentication with the Prerender service.
* This parameter is deprecated and will be removed in a future release.
* Please use the `tokenUrlAssociation` parameter instead.
* *If `tokenUrlAssociation` is provided, `tokenList` will be ignored*
*/
tokenList: Array<string>;
/**
* The ARN of the SSL certificate to use for HTTPS connections.
*/
Expand Down Expand Up @@ -79,6 +72,13 @@ export interface PrerenderFargateOptions {
* @default - false
*/
enableS3Endpoint?: boolean;
/**
* A pre-configured AWS SSM Parameter Store parameter can be used for Prerender API tokens.
* Prerender ECS service checks the [token] value to validate the requests.
* Parameter type: StringList
* Value: Comma-separated token list
*/
tokenParam?: string;
/**
* Configuration for associating tokens with specific domain URLs.
* During the reacaching process, these tokens will be used to validate the request.
Expand All @@ -98,6 +98,13 @@ export interface PrerenderFargateOptions {
* ```
*/
tokenUrlAssociation?: PrerenderTokenUrlAssociationOptions;
/**
* A list of tokens to use for authentication with the Prerender service.
* This parameter is deprecated and will be removed in a future release.
* Please use the `tokenUrlAssociation` parameter instead.
* *If `tokenUrlAssociation` is provided, `tokenList` will be ignored*
*/
tokenList?: Array<string>;
/**
* Prerender Fargate Scaling option
* This allows to alter the scaling behavior. The default configuration should be sufficient
Expand All @@ -110,6 +117,11 @@ export interface PrerenderFargateOptions {
* @default - { maxConcurrentExecutions: 1 }
*/
prerenderFargateRecachingOptions?: PrerenderFargateRecachingOptions;
/**
* Enable Re-caching API
* @default - true
*/
enableRecache?: boolean;
}

/**
Expand Down
90 changes: 55 additions & 35 deletions packages/prerender-fargate/lib/prerender-fargate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Construct } from "constructs";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ecsPatterns from "aws-cdk-lib/aws-ecs-patterns";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ssm from "aws-cdk-lib/aws-ssm";
import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
import { HostedZone } from "aws-cdk-lib/aws-route53";
import { Bucket, BlockPublicAccess } from "aws-cdk-lib/aws-s3";
Expand Down Expand Up @@ -49,19 +50,7 @@ import { PrerenderFargateOptions } from "./prerender-fargate-options";
* enableRedirectCache: 'false',
* maxInstanceCount: 2,
* enableS3Endpoint: true,
* tokenUrlAssociation: {
* tokenUrlAssociation: {
* token1: [
* "https://example.com",
* "https://acme.example.com"
* ],
* token2: [
* "https://example1.com",
* "https://acme.example1.com"
* ]
* },
* ssmPathPrefix: "/prerender/recache/tokens"
* }
* tokenParam: '/prerender/tokens'
* });
* ```
*
Expand All @@ -85,6 +74,7 @@ export class PrerenderFargate extends Construct {
super(scope, id);

const {
tokenParam,
tokenUrlAssociation,
certificateArn,
maxInstanceCount,
Expand All @@ -100,6 +90,7 @@ export class PrerenderFargate extends Construct {
minInstanceCount,
prerenderFargateScalingOptions,
prerenderFargateRecachingOptions,
enableRecache,
} = props;

// Create bucket for prerender storage
Expand Down Expand Up @@ -139,13 +130,48 @@ export class PrerenderFargate extends Construct {
}
);

/**
* This provide backward compatibility for the tokenList property
* If tokenUrlAssociation is provided, tokenList will be ignored
*/
const tokenList = tokenUrlAssociation
? Object.keys(tokenUrlAssociation.tokenUrlAssociation)
: props.tokenList.toString();
// Build ECS service taskImageOption
let secrets = {};
const environment = {
S3_BUCKET_NAME: this.bucket.bucketName,
AWS_REGION: Stack.of(this).region,
ENABLE_REDIRECT_CACHE: enableRedirectCache || "false",
TOKEN_LIST: "",
};
if (tokenParam) {
/**
* tokenParam is the name of the SSM Parameter that has a stringList of tokens as its value.
* If tokenParam is present, which is the better security practice, it will override tokenList and/or tokenUrlAssociation.
* Tokens for Recache API doesn't need to be fed via Stack, as it should have its own SSM parameter(s).
*/
secrets = {
/**
* secrets and environment properties of taskImageOption can't have the same env var name defined, hence TOKEN_LIST_SSM is used here.
* TOKEN_LIST_SSM and TOKEN_LIST are handled within the application, i.e. server.js
*/
TOKEN_LIST_SSM: ecs.Secret.fromSsmParameter(
ssm.StringListParameter.fromStringListParameterName(
this,
"token",
tokenParam
)
),
};
} else if (tokenUrlAssociation || props.tokenList) {
/**
* This provide backward compatibility for the tokenList and tokenUrlAssociation properties.
* If tokenUrlAssociation is provided, tokenList will be ignored
*/
let tokenList = tokenUrlAssociation
? Object.keys(tokenUrlAssociation.tokenUrlAssociation)
: props.tokenList;
if (!tokenList) tokenList = [""]; // To suppress the error in the next line about this value being potentially undefined.
environment.TOKEN_LIST = tokenList.toString();
} else {
console.error(
"Either one of tokenParam, tokenUrlAssociation, or tokenList must be provided."
);
}

// Create a load-balanced Fargate service
const fargateService =
Expand All @@ -159,15 +185,12 @@ export class PrerenderFargate extends Construct {
cpu: instanceCPU || 512, // 0.5 vCPU default
memoryLimitMiB: instanceMemory || 1024, // 1 GB default to give Chrome enough memory
taskImageOptions: {
containerName: `${prerenderName}-container`,
image: ecs.ContainerImage.fromDockerImageAsset(asset),
enableLogging: true,
containerPort: 3000,
environment: {
S3_BUCKET_NAME: this.bucket.bucketName,
AWS_REGION: Stack.of(this).region,
ENABLE_REDIRECT_CACHE: enableRedirectCache || "false",
TOKEN_LIST: tokenList.toString(),
},
environment,
secrets,
},
publicLoadBalancer: true,
assignPublicIp: true,
Expand Down Expand Up @@ -234,10 +257,6 @@ export class PrerenderFargate extends Construct {
});
}

/**
* Recache API
* Recaching is enable by default
*/
if (tokenUrlAssociation) {
/**
* Create the token-url association
Expand All @@ -251,14 +270,15 @@ export class PrerenderFargate extends Construct {
ssmPathPrefix: tokenUrlAssociation.ssmPathPrefix,
}
);
}

/**
* Create the recache API
* This would create the API that is used to trigger recaching of the URLs
*/
/**
* Recache API is enable by default
* This would create the API that is used to trigger recaching of the URLs
*/
if (enableRecache === undefined || enableRecache) {
new PrerenderRecacheApi(this, `${prerenderName}-recache-api`, {
prerenderS3Bucket: this.bucket,
tokenList: Object.keys(tokenUrlAssociation.tokenUrlAssociation),
maxConcurrentExecutions:
prerenderFargateRecachingOptions?.maxConcurrentExecutions || 1,
});
Expand Down
5 changes: 4 additions & 1 deletion packages/prerender-fargate/lib/prerender/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const server = prerender({
chromeLocation: '/usr/bin/chromium-browser'
});

// Prefer tokens defined via SSM
const tokens = process.env.TOKEN_LIST_SSM ? process.env.TOKEN_LIST_SSM : process.env.TOKEN_LIST;
const tokenAllowList = tokens.toString().split(',');

server.use({
requestReceived: (req, res, next) => {
// Log "x-prerender-user-agent" value forwarded from CloudFront/Lambda@edge that contains the original User-Agent value. If not present, e.g. requests from ELB, default to "user-agent" value.
Expand All @@ -34,7 +38,6 @@ server.use({
}

// compare credentials in header to list of allowed credentials
const tokenAllowList = process.env.TOKEN_LIST.toString().split(',');

let authenticated = false;
for (const token of tokenAllowList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ export interface PrerenderRecacheApiOptions {
* The S3 bucket where prerendered pages are stored.
*/
prerenderS3Bucket: Bucket;
/**
* A list of tokens used to authenticate API requests.
*/
tokenList: string[];
/**
* Maximum number of concurrent executions of the Prerender Recache API.
*/
Expand All @@ -44,8 +40,6 @@ export class PrerenderRecacheApi extends Construct {
proxy: false,
});

options.tokenList.forEach(k => this.api.addApiKey(k));

const recache = this.api.root.addResource("recache");
recache.addMethod("POST");

Expand Down
2 changes: 1 addition & 1 deletion packages/prerender-fargate/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aligent/cdk-prerender-fargate",
"version": "0.0.1",
"version": "2.3.0-beta",
"description": "A construct to host Prerender in Fargate",
"main": "index.js",
"scripts": {
Expand Down

0 comments on commit b7b423d

Please sign in to comment.