UNPKG

@collaborne/custom-cloudformation-resources

Version:
180 lines (151 loc) 4.04 kB
import { WAFV2 } from 'aws-sdk'; import { SchemaType } from '@collaborne/json-schema-to-type'; import { CustomResource, Response } from '../custom-resource'; import { Logger } from '../logger'; import { Rules } from 'aws-sdk/clients/wafv2'; const SCHEMA = { type: 'object' as const, properties: { Name: { type: 'string' as const, }, Description: { type: 'string' as const, }, Capacity: { type: 'number' as const, }, Rules: { type: 'string' as const, }, }, required: ['Name', 'Capacity'], }; const SCOPE = 'CLOUDFRONT'; interface ResourceAttributes { Arn: string; Id: string; } export class CloudFrontWafV2RuleGroup extends CustomResource< ResourceAttributes, typeof SCHEMA > { region = 'us-east-1'; // Region must be `us-east-1` for `CloudFront WAF` private wafV2 = new WAFV2({ region: this.region, }); constructor(logicalResourceId: string, logger: Logger) { super(SCHEMA, logicalResourceId, logger); } public async createResource( physicalResourceId: string, { Name, Description, Capacity, Rules }: SchemaType<typeof SCHEMA>, ): Promise<Response<ResourceAttributes>> { const rules: Rules = JSON.parse(Rules); const ruleGroupParams = { Name, Description, Scope: SCOPE, Capacity, Rules: rules, VisibilityConfig: { SampledRequestsEnabled: true, CloudWatchMetricsEnabled: true, MetricName: 'CommonAttackRuleGroup', }, }; try { const ruleGroup = await this.wafV2 .createRuleGroup(ruleGroupParams) .promise(); if (!ruleGroup || !ruleGroup.Summary?.ARN || !ruleGroup.Summary.Id) { throw new Error('Error creating Rule Group'); } console.debug('Rule Group created successfully:', ruleGroup.Summary?.ARN); return { physicalResourceId, attributes: { Arn: ruleGroup.Summary?.ARN, Id: ruleGroup.Summary?.Id, }, }; } catch (err) { console.error('Error creating Rule Group:', err); throw new Error('Error creating Rule Group'); } } public async deleteResource( physicalResourceId: string, { Name }: SchemaType<typeof SCHEMA>, ): Promise<Response<ResourceAttributes>> { const ruleGroup = await this.getRuleGroup(Name); if (!ruleGroup.RuleGroup || !ruleGroup.LockToken) { throw new Error(`Rule group is not deletable ${Name}`); } await this.wafV2 .deleteRuleGroup({ Name, Scope: SCOPE, Id: ruleGroup.RuleGroup?.Id, LockToken: ruleGroup.LockToken, }) .promise(); return { physicalResourceId, }; } public async updateResource( physicalResourceId: string, { Name, Description, Rules }: SchemaType<typeof SCHEMA>, ): Promise<Response<ResourceAttributes>> { const ruleGroup = await this.getRuleGroup(Name); if (!ruleGroup.RuleGroup || !ruleGroup.LockToken) { throw new Error(`Rule group is not updatable ${Name}`); } const rules: Rules = JSON.parse(Rules); const updateRuleGroupParams = { Id: ruleGroup.RuleGroup.Id, Name, Description, Scope: SCOPE, Rules: rules, VisibilityConfig: ruleGroup.RuleGroup.VisibilityConfig, LockToken: ruleGroup.LockToken, }; await this.wafV2.updateRuleGroup(updateRuleGroupParams).promise(); return { physicalResourceId, attributes: { Arn: ruleGroup.RuleGroup.ARN, Id: ruleGroup.RuleGroup.Id, }, }; } protected async getRuleGroup( name: string, ): Promise<WAFV2.GetRuleGroupResponse> { const ruleGroups = await this.wafV2 .listRuleGroups({ Scope: SCOPE }) .promise(); if (!ruleGroups.RuleGroups || ruleGroups.RuleGroups.length < 1) { throw new Error( `There is no rule groups in your account with scope ${SCOPE}`, ); } const ruleGroupSummary = ruleGroups.RuleGroups.find( ruleGroup => ruleGroup.Name === name, ); if (!ruleGroupSummary) { throw new Error(`Unknown rule group name ${name}`); } const ruleGroup = await this.wafV2 .getRuleGroup({ Name: name, Id: ruleGroupSummary.Id, ARN: ruleGroupSummary.ARN, Scope: SCOPE, }) .promise(); return ruleGroup; } }