Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions deploy/cloudformation/edge-stack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Lowkey WebUI Cognito Auth — Lambda@Edge companion stack.
Deploys the Lambda@Edge function, its version, IAM role, and the two
Secrets Manager secrets (signing key + merged edge config) in us-east-1
because Lambda@Edge sources MUST live in us-east-1 (AWS platform
requirement). The main Lowkey stack can deploy in any region; the
installer wires the two together via CFN parameters and cross-region
Secrets Manager writes.

Parameters:
EnvironmentName:
Type: String
MinLength: 1
Description: "Environment name — matches the main stack's EnvironmentName so secret names align."

PackName:
Type: String
Default: kirocrew
Description: "Pack that owns this edge Lambda. Used only for tags."

EdgeLambdaS3Bucket:
Type: String
MinLength: 1
Description: "S3 bucket (in us-east-1) holding the Lambda@Edge deployment zip."

EdgeLambdaS3Key:
Type: String
MinLength: 1
Description: "S3 key of the Lambda@Edge deployment zip."

EdgeLambdaCodeSha256:
Type: String
MinLength: 1
Description: "Base64 SHA256 of the deployment zip. Forces a new AWS::Lambda::Version when code changes."

Rules:
WebUIEdgeRequiresUsEast1:
Assertions:
- Assert: !Equals [!Ref 'AWS::Region', 'us-east-1']
AssertDescription: "Lambda@Edge companion stack must be deployed in us-east-1."

Resources:
# Raw HMAC signing key. CFN-managed via GenerateSecretString. Never written
# to by anything else — read once by the main-stack Custom Resource which
# then merges it into EdgeConfigSecret alongside the Cognito pool/client/domain.
WebUIEdgeSigningKeySecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub '/lowkey/${EnvironmentName}/webui-edge-signing-key'
Description: !Sub 'Raw HMAC signing key for KiroCrew WebUI Lambda@Edge (${EnvironmentName})'
GenerateSecretString:
SecretStringTemplate: '{}'
GenerateStringKey: 'key'
PasswordLength: 64
ExcludePunctuation: true
Tags:
- Key: loki:managed
Value: 'true'
- Key: loki:pack
Value: !Ref PackName
- Key: loki:env
Value: !Ref EnvironmentName

# Merged {poolId, clientId, cognitoDomain, signingKey} secret that the
# Lambda@Edge reads at cold start. Initial SecretString is a placeholder;
# the main-stack Custom Resource overwrites it once the Cognito pool exists.
WebUIEdgeConfigSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub '/lowkey/${EnvironmentName}/webui-edge-config'
Description: !Sub 'Merged Cognito config for KiroCrew WebUI Lambda@Edge (${EnvironmentName})'
SecretString: '{"poolId":"pending","clientId":"pending","cognitoDomain":"pending","signingKey":"pending"}'
Tags:
- Key: loki:managed
Value: 'true'
- Key: loki:pack
Value: !Ref PackName
- Key: loki:env
Value: !Ref EnvironmentName

WebUIEdgeLambdaRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub '${EnvironmentName}-webui-edge-role'
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- edgelambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: FetchEdgeConfig
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref WebUIEdgeConfigSecret

WebUIEdgeLambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub '${EnvironmentName}-webui-edge-auth'
Runtime: nodejs22.x
Handler: index.handler
MemorySize: 128
Timeout: 5
Role: !GetAtt WebUIEdgeLambdaRole.Arn
Code:
S3Bucket: !Ref EdgeLambdaS3Bucket
S3Key: !Ref EdgeLambdaS3Key

WebUIEdgeLambdaVersion:
Type: AWS::Lambda::Version
# Old versions cannot be deleted while CloudFront still references them
# (Lambda@Edge replicas take ~1hr to GC after CloudFront disassociates).
# Retain them on stack update/delete so a code refresh doesn't roll back
# the edge stack. Old versions are free — they accumulate harmlessly.
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
FunctionName: !Ref WebUIEdgeLambdaFunction
Description: !Sub 'KiroCrew WebUI Cognito auth (${EnvironmentName}) sha256=${EdgeLambdaCodeSha256}'
CodeSha256: !Ref EdgeLambdaCodeSha256
Comment on lines +120 to +131

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Retain associated edge versions during companion-stack updates

When EdgeLambdaCodeSha256 changes, CloudFormation replaces this version and then attempts to delete the old version while the separately deployed main stack's CloudFront distribution still references it. Lambda rejects deletion of a replicated Lambda@Edge version, so the edge-stack update can fail before the installer gets the new ARN and updates the main stack. Add an update-retention strategy for old versions or otherwise sequence association removal/update before deleting them.

Useful? React with 👍 / 👎.


Outputs:
EdgeLambdaVersionArn:
Description: "Versioned ARN of the Lambda@Edge function. Pass this to the main stack's WebUIEdgeLambdaVersionArn parameter."
Value: !Ref WebUIEdgeLambdaVersion

EdgeLambdaFunctionArn:
Description: "Unversioned ARN of the Lambda@Edge function."
Value: !GetAtt WebUIEdgeLambdaFunction.Arn

EdgeConfigSecretName:
Description: "Deterministic name of the merged edge config secret. Custom Resource in the main stack uses this via a cross-region Secrets Manager client."
Value: !Sub '/lowkey/${EnvironmentName}/webui-edge-config'

EdgeConfigSecretArn:
Description: "Full ARN of the edge config secret."
Value: !Ref WebUIEdgeConfigSecret

SigningKeySecretName:
Description: "Deterministic name of the raw signing key secret."
Value: !Sub '/lowkey/${EnvironmentName}/webui-edge-signing-key'

SigningKeySecretArn:
Description: "Full ARN of the signing key secret."
Value: !Ref WebUIEdgeSigningKeySecret
Loading