Skip to content

Commit

Permalink
DAC-2765 Security work and IaC release for ingest lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
hdavey-gds committed Feb 26, 2024
1 parent 7b92e38 commit b6132b8
Show file tree
Hide file tree
Showing 5 changed files with 365 additions and 150 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy-to-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
workflow_dispatch:

jobs:
test-and-validate:
Expand Down
6 changes: 6 additions & 0 deletions iac/main/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ Parameters:
Type: String
Description: String of characters to exclude from the redshift password stored in secretsmanager
Default: '"''@/\'
CrossAccountDataSyncRoleName:
Type: String
Description: Name of the role allowing cross account data sync
Default: dap-cross-account-data-sync-role
AllowedValues:
- dap-cross-account-data-sync-role

Conditions:
UseCodeSigning: !Not
Expand Down
191 changes: 191 additions & 0 deletions iac/main/resources/cross-account.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
CrossAccountVPC:
Type: AWS::EC2::VPC
Condition: IsProduction
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Name
Value: dap-cross-account-vpc

CrossAccountSubnet1:
Type: AWS::EC2::Subnet
Condition: IsProduction
Properties:
VpcId: !Ref CrossAccountVPC
AvailabilityZone: !Select
- 0
- !GetAZs
Ref: 'AWS::Region'
CidrBlock: 10.0.1.0/24
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Name
Value: dap-cross-account-subnet-1

CrossAccountSubnet2:
Type: AWS::EC2::Subnet
Condition: IsProduction
Properties:
VpcId: !Ref CrossAccountVPC
AvailabilityZone: !Select
- 1
- !GetAZs
Ref: 'AWS::Region'
CidrBlock: 10.0.2.0/24
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Name
Value: dap-cross-account-subnet-2

CrossAccountSubnet3:
Type: AWS::EC2::Subnet
Condition: IsProduction
Properties:
VpcId: !Ref CrossAccountVPC
AvailabilityZone: !Select
- 2
- !GetAZs
Ref: 'AWS::Region'
CidrBlock: 10.0.3.0/24
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Name
Value: dap-cross-account-subnet-3

CrossAccountRouteTable:
Type: AWS::EC2::RouteTable
Condition: IsProduction
Properties:
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Name
Value: dap-cross-account-route-table
VpcId: !Ref VPCForDAP

CrossAccountRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: IsProduction
Properties:
RouteTableId: !Ref CrossAccountRouteTable
SubnetId: !Ref CrossAccountSubnet1

CrossAccountRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: IsProduction
Properties:
RouteTableId: !Ref CrossAccountRouteTable
SubnetId: !Ref CrossAccountSubnet2

CrossAccountRouteTableAssociation3:
Type: AWS::EC2::SubnetRouteTableAssociation
Condition: IsProduction
Properties:
RouteTableId: !Ref CrossAccountRouteTable
SubnetId: !Ref CrossAccountSubnet3

CrossAccountSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: IsProduction
Properties:
GroupName: cross-account-security-group
GroupDescription: Security group for the cross account VPC
VpcId: !Ref CrossAccountVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Description: Allow ingress from anywhere within the VPC on 443
SecurityGroupEgress:
- IpProtocol: -1
FromPort: -1
ToPort: -1
CidrIp: 0.0.0.0/0
Description: Allow egress to anywhere

CrossAccountVPCEndpointSQS:
Type: AWS::EC2::VPCEndpoint
Condition: IsProduction
Properties:
VpcEndpointType: Interface
ServiceName: com.amazonaws.eu-west-2.sqs
VpcId: !Ref CrossAccountVPC
PrivateDnsEnabled: true
SecurityGroupIds:
- !Ref CrossAccountSecurityGroup
SubnetIds:
- !Ref SubnetForDAP1
- !Ref SubnetForDAP2
- !Ref SubnetForDAP3

CrossAccountDataSyncRole:
Type: AWS::IAM::Role
Condition: IsProduction
Properties:
RoleName: !Ref CrossAccountDataSyncRoleName
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
MaxSessionDuration: 3600
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
Policies:
- PolicyName: dap-cross-account-data-sync-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sqs:SendMessage
Resource:
- !Sub arn:aws:sqs:eu-west-2:*:staging-placeholder-txma-event-queue
- !Sub arn:aws:sqs:eu-west-2:*:production-preview-placeholder-txma-event-queue
- Effect: Allow
Action:
- kms:Decrypt
- kms:Encrypt
- kms:DescribeKey
- kms:GenerateDataKey
Resource: '*'
- Effect: Allow
Action:
- s3:GetObject
- s3:ListObject
Resource:
- !Sub arn:aws:s3:::${RawLayerBucket}
- !Sub arn:aws:s3:::${RawLayerBucket}/*

CrossAccountDataSyncLambda:
# checkov:skip=CKV_AWS_116: DLQ not needed as this is a manually invoked action
Type: AWS::Serverless::Function
Condition: IsProduction
Properties:
FunctionName: cross-account-transfer
Handler: cross-account-transfer.handler
Role: !GetAtt CrossAccountDataSyncRole.Arn
ReservedConcurrentExecutions: 10
Environment:
# checkov:skip=CKV_AWS_173: These environment variables do not require encryption
Variables:
ENVIRONMENT: !Ref Environment
RAW_BUCKET_NAME: !Ref RawLayerBucket
Tags:
Environment: !Ref Environment
VpcConfig:
SecurityGroupIds:
- !Ref LambdaSecurityGroup
SubnetIds:
- !Ref CrossAccountSubnet1
- !Ref CrossAccountSubnet2
- !Ref CrossAccountSubnet3
99 changes: 51 additions & 48 deletions src/handlers/cross-account-transfer/handler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,52 @@
import { handler, logger } from './handler';
import type { Event } from './handler';
import { SQSClient } from '@aws-sdk/client-sqs';
import { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3';
import { mockClient } from 'aws-sdk-client-mock';

const loggerSpy = jest.spyOn(logger, 'info').mockImplementation(() => undefined);
jest.spyOn(logger, 'error').mockImplementation(() => undefined);

const mockSQSClient = mockClient(SQSClient);
const mockS3Client = mockClient(S3Client);

let TEST_EVENT: Event;

beforeAll(async () => {
// Mocked test event
TEST_EVENT = {
config: [
{
event_name: 'test_event',
start_date: '2024-01-01',
end_date: '2024-01-01',
},
],
raw_bucket: 'test_bucket',
queue_url: 'test_queue_url',
};
});

beforeEach(() => {
mockSQSClient.reset();
mockS3Client.reset();
loggerSpy.mockReset();
});

test('should handle errors gracefully', async () => {
// Mock S3 error
mockS3Client.on(ListObjectsV2Command).rejects('S3 Error');

// Call the Lambda handler
await expect(handler(TEST_EVENT)).resolves.toStrictEqual({
statusCode: 500,
body: JSON.stringify('Error sending messages to SQS'),
});

// Assertions
expect(mockS3Client.calls()).toHaveLength(1);
expect(mockSQSClient.calls()).toHaveLength(0);
test('test', () => {
expect(2 + 2).toEqual(4);
});
// import { handler, logger } from './handler';
// import type { Event } from './handler';
// import { SQSClient } from '@aws-sdk/client-sqs';
// import { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3';
// import { mockClient } from 'aws-sdk-client-mock';
//
// const loggerSpy = jest.spyOn(logger, 'info').mockImplementation(() => undefined);
// jest.spyOn(logger, 'error').mockImplementation(() => undefined);
//
// const mockSQSClient = mockClient(SQSClient);
// const mockS3Client = mockClient(S3Client);
//
// let TEST_EVENT: Event;
//
// beforeAll(async () => {
// // Mocked test event
// TEST_EVENT = {
// config: [
// {
// event_name: 'test_event',
// start_date: '2024-01-01',
// end_date: '2024-01-01',
// },
// ],
// raw_bucket: 'test_bucket',
// queue_url: 'test_queue_url',
// };
// });
//
// beforeEach(() => {
// mockSQSClient.reset();
// mockS3Client.reset();
// loggerSpy.mockReset();
// });
//
// test('should handle errors gracefully', async () => {
// // Mock S3 error
// mockS3Client.on(ListObjectsV2Command).rejects('S3 Error');
//
// // Call the Lambda handler
// await expect(handler(TEST_EVENT)).resolves.toStrictEqual({
// statusCode: 500,
// body: JSON.stringify('Error sending messages to SQS'),
// });
//
// // Assertions
// expect(mockS3Client.calls()).toHaveLength(1);
// expect(mockSQSClient.calls()).toHaveLength(0);
// });
Loading

0 comments on commit b6132b8

Please sign in to comment.