diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1b5f27f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "" +labels: bug +assignees: "" +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Please complete the following information about the solution:** + +- [ ] Version: [e.g. v1.0.0] + +To get the version of the solution, you can look at the description of the created CloudFormation stack. For example, "_(SO0108) - AWS Network Firewall Deployment Automations for AWS Transit Gateway. Version **v1.0.0**_". + +- [ ] Region: [e.g. us-east-1] +- [ ] Was the solution modified from the version published on this repository? +- [ ] If the answer to the previous question was yes, are the changes available on GitHub? +- [ ] Have you checked your [service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) for the sevices this solution uses? +- [ ] Were there any errors in the CloudWatch Logs? + +**Screenshots** +If applicable, add screenshots to help explain your problem (please **DO NOT include sensitive information**). + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..8c46516 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: Feature request +about: Suggest an idea for this solution +title: "" +labels: enhancement +assignees: "" +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..db6ceed --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +_Issue #, if available:_ + +_Description of changes:_ + +By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2093049 --- /dev/null +++ b/.gitignore @@ -0,0 +1,108 @@ + +*node_modules* + +# C extensions +*.so +*.pyc +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject + +#cdk +*cdk.out* +*.d.ts +*.js + +#ignore these in the deployment folder +*regional-s3-assets* +*staging* +*global-s3-assets* +.DS_Store +*.zip +deployment/open-source +deployment/examples +deployment/dist +source/deploy +deployment/vpc_rules + + +.env +.idea +.vscode +source/scratch/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100755 index 0000000..8dacba3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2021-02-24 +### Added +- New solution AWS Network Firewall Deployment Automations for AWS Transit Gateway, initial version diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md old mode 100644 new mode 100755 index 5b627cf..3b64466 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,4 @@ ## Code of Conduct -This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). -For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md old mode 100644 new mode 100755 index c4b6a1c..67f2886 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ information to effectively respond to your bug report or contribution. We welcome you to use the GitHub issue tracker to report bugs or suggest features. -When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already +When filing an issue, please check [existing open](https://github.com/awslabs/network-firewall-automation/issues), or [recently closed](https://github.com/awslabs/network-firewall-automation/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: * A reproducible test case or series of steps @@ -23,7 +23,7 @@ reported the issue. Please try to include as much information as you can. Detail ## Contributing via Pull Requests Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: -1. You are working against the latest source on the *main* branch. +1. You are working against the latest source on the *master* branch. 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. @@ -31,17 +31,18 @@ To send us a pull request, please: 1. Fork the repository. 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. -3. Ensure local tests pass. -4. Commit to your fork using clear commit messages. -5. Send us a pull request, answering any default questions in the pull request interface. -6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. +3. Ensure all build processes execute successfully (see README.md for additional guidance). +4. Ensure all unit, integration, and/or snapshot tests pass, as applicable. +5. Commit to your fork using clear commit messages. +6. Send us a pull request, answering any default questions in the pull request interface. +7. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). ## Finding contributions to work on -Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/network-firewall-automation/labels/help%20wanted) issues is a great place to start. ## Code of Conduct @@ -51,9 +52,11 @@ opensource-codeofconduct@amazon.com with any additional questions or comments. ## Security issue notifications -If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public GitHub issue. ## Licensing -See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. +See the [LICENSE](https://github.com/awslabs/network-firewall-automation/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. + +We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/LICENSE b/LICENSE.txt old mode 100644 new mode 100755 similarity index 99% rename from LICENSE rename to LICENSE.txt index 67db858..19dc35b --- a/LICENSE +++ b/LICENSE.txt @@ -172,4 +172,4 @@ of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 616fc58..0000000 --- a/NOTICE +++ /dev/null @@ -1 +0,0 @@ -Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100755 index 0000000..68807d3 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,20 @@ +AWS Network Firewall Automation +Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except +in compliance with the License. A copy of the License is located at http://www.apache.org/licenses/ +or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the +specific language governing permissions and limitations under the License. + +********************** +THIRD PARTY COMPONENTS +********************** +This software includes third party software subject to the following copyrights: +jest undert the MIT License +axios under the MIT License +moment under the MIT License +uuid under the MIT License. +AWS SDK under the Apache License Version 2.0 +aws-cdk under Apache License 2.0 + +AWS SDK under the Apache License Version 2.0 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 847260c..eea0150 --- a/README.md +++ b/README.md @@ -1,17 +1,177 @@ -## My Project +**[AWS Network Firewall Deployment Automations for AWS Transit Gateway](https://aws.amazon.com/solutions/implementations/aws-network-firewall-deployment-automations-for-aws-transit-gateway)** | **[🚧 Feature request](https://github.com/awslabs/aws-network-firewall-deployment-automations-for-aws-transit-gateway/issues/new?assignees=&labels=feature-request%2C+enhancement&template=feature_request.md&title=)** | **[🐛 Bug Report](https://github.com/awslabs/aws-network-firewall-deployment-automations-for-aws-transit-gateway/issues/new?assignees=&labels=bug%2C+triage&template=bug_report.md&title=)** -TODO: Fill this README out! +Note: If you want to use the solution without building from source, navigate to Solution Landing Page -Be sure to: +## Table of contents -* Change the title in this README -* Edit your repository description on GitHub +- [Solution Overview](#solution-overview) +- [Architecture Diagram](#architecture-diagram) +- [AWS CDK Constructs](#aws-solutions-constructs) +- [Customizing the Solution](#customizing-the-solution) + - [Prerequisites for Customization](#prerequisites-for-customization) + - [Build](#build) + - [Unit Test](#unit-test) + - [Deploy](#deploy) +- [File Structure](#file-structure) +- [License](#license) -## Security + +# Solution Overview +[//]: # Solution for AWS Network Firewall Deployment Automations for AWS Transit Gateway. + + +# Architecture Diagram +[//]: # ![Architecture Diagram](./source/architecture.png) + + +## Prerequisites for Customization +[//]: # Node.js>12 + + +## Build +[//]: # Build the CDK code +``` +cd source/ +npm run build +``` + +Build the Network Firewall Solution CodeBuild source code +``` +cd source/networkfirewallAutomation +tsc +``` + +Build the templates for custom deployments + +``` +cd deployments/ +chmod +x ./build-s3-dist.sh +./build-s3-dist.sh [SOLUTION_DIST_BUCKET] network-firewall-automation [VERSION_ID] +``` + + +## Unit Test +[//]: # Run the unit tests + +``` +cd source/ +chmod +x ./run-all-tests.sh +``` + + +## Deploy +[//]: Follow the steps for deploying your custom version of the solution. +* Create an S3 bucket with the bucket appended with the region in which the deployment is to be made. example, if the deployment is to be made in us-east-1 create a bucket name as [BUCKET_NAME]-us-east-1. +* Create the distribution files using the script provided in the build section above. +* Create the S3 Key in the bucket network-firewall-automation/[VERSION_ID]/ +* Create the S3 Key in the bucket network-firewall-automation/latest/ +* Copy the file ./deployment/regional-s3-assets/network-firewall-automation.zip to the location s3://[BUCKET_NAME]-[REGION]/network-firewall-automation/[VERSION_ID]/ +* Copy the file ./deployment/regional-s3-assets/network-firewall-configuration.zip to the location s3://[BUCKET_NAME]-[REGION]/network-firewall-automation/latest/ + +Once the above steps are completed, use the file ./deployment/global-s3-assets/aws-network-firewall-deployment-automations-for-aws-transit-gateway.template to create a stack in CloudFormation. + + + +# File structure + +aws-network-firewall-deployment-automations-for-aws-transit-gateway consists of: + +- CDK constructs to generate necessary resources +- Microservices used in the solution + +[//]: # File Structure + +
+|-deployment/ + |build-s3-dist.sh/ [ Build script for create the distribution for the solution.] +|-source/ + |-bin/ + |-network-firewall-auto-solution.ts [ entry point for CDK app ] + |-test/ [ unit tests for CDK constructs ] + |-network-firewall-automation-solution.test.ts [CDK construct for the solution.] + |-__snapshots__ + |-network-firewall-automation-solution.test.ts.snap [CDK construct template snapshot of unit testing.] + |-lib/ + |-network-firewall-automation-solution-stack.ts [ CDK construct for the solution. ] + |-networkFirewallAutomation + |-__tests__ + |-firewall-test-configuration + |-firewalls + |-firewall-invalid.json + |-firewall-nopolicy.json + |-firewall-example.json + |-firewallPolicies + |-firewall-invalid-policy.json + |-firewall-policy-2.json + |-firewall-policy.example.json + |-ruleGroups + |-stateless-pass-action.example.json + |-stateless-fwd-to-stateful.example.json + |-stateful-domainblock.example.json + |-drop.rules + |-suricata-rule-reference.json + |-network-firewall-service.spec.ts + |-ec2-manager.spec.ts + |-firewall-config-validation.spec.ts + |-network-firewall-manager.spec.ts + |-send-metrics.spec.ts + |-config + |-examples + |-firewalls + |-firewall.example.json + |-firewallPolicies + |-firewall-policy.example.json + |-ruleGroups + |-stateless-pass-action.example.json + |-stateless-fwd-to-stateful.example.json + |-stateful-domainblock.example.json + |-drop.rules + |-suricata-rule-reference.json + |-firewallPolicies + |-firewall-policy-1.json + |-firewalls + |-firewall-1.json + |-lib + |-ec2-manager.ts + |-network-firewall-manager.ts + |-common + |-configReader + |-config-reader.ts + |-logger.ts + |-stringUtils.ts + |-firewall-config-validation.ts + |-send-metrics.ts + |-service + |-awsClientConfig.ts + |-ec2-service.ts + |-network-firewall-service.ts + |-build.ts + |-index.ts + |-config_files [ tsconfig, jest.config.js, package.json etc. ] + |-config_files [ tsconfig, cdk.json, package.json etc. ] + |-run-all-tests.sh +|-buildspec.yml +|-architecture.yml +|-CHANGELOG.md +|-CODE_OF_CONDUCT.md +|-LICENSE.txt +|-CONTRIBUTING.md +|-NOTICE.txt ++ + +*** + +Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at + + http://www.apache.org/licenses/ + +or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and limitations under the License. + +See [LICENSE](https://github.com/awslabs/aws-network-firewall-solution-for-aws-transit-gateway/blob/master/LICENSE.txt) -See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. -## License -This project is licensed under the Apache-2.0 License. diff --git a/deployment/build-s3-dist.sh b/deployment/build-s3-dist.sh new file mode 100755 index 0000000..55cdd43 --- /dev/null +++ b/deployment/build-s3-dist.sh @@ -0,0 +1,142 @@ +#!/bin/bash +# +# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance +# with the License. A copy of the License is located at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES +# OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions +# and limitations under the License. +# + +# Important: CDK global version number +cdk_version=1.77.0 + +# Check to see if the required parameters have been provided: +if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then + echo "Please provide the base source bucket name, trademark approved solution name and version where the artifact code will eventually reside." + echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v1.0.0" + exit 1 +fi + +[ "$DEBUG" == 'true' ] && set -x +set -e + +# Environment variables +export DIST_VERSION=$3 +export DIST_OUTPUT_BUCKET=$1 +export SOLUTION_ID=SO0108 +export SOLUTION_NAME=$2 +export SOLUTION_TRADEMARKEDNAME=$2 + + +# Get reference for all important folders +template_dir="$PWD" +staging_dist_dir="$template_dir/staging" +template_dist_dir="$template_dir/global-s3-assets" +build_dist_dir="$template_dir/regional-s3-assets" +source_dir="$template_dir/../source" + +echo "------------------------------------------------------------------------------" +echo "[Init] Remove any old dist files from previous runs" +echo "------------------------------------------------------------------------------" + +echo "rm -rf $template_dist_dir" +rm -rf $template_dist_dir +echo "mkdir -p $template_dist_dir" +mkdir -p $template_dist_dir +echo "rm -rf $build_dist_dir" +rm -rf $build_dist_dir +echo "mkdir -p $build_dist_dir" +mkdir -p $build_dist_dir +echo "rm -rf $staging_dist_dir" +rm -rf $staging_dist_dir +echo "mkdir -p $staging_dist_dir" +mkdir -p $staging_dist_dir +echo "rm -rf $template_dir/vpc_rules" +rm -rf $template_dir/vpc_rules + +echo "------------------------------------------------------------------------------" +echo "[Synth] CDK Project" +echo "------------------------------------------------------------------------------" + +# Install the global aws-cdk package +echo "cd $source_dir" +cd $source_dir +echo "npm install -g aws-cdk@$cdk_version" +npm install -g aws-cdk@$cdk_version + +# Run 'cdk synth' to generate raw solution outputs +cd "$source_dir" +echo "cdk synth --output=$staging_dist_dir" +npm run build && cdk synth --output=$staging_dist_dir + +# Remove unnecessary output files +echo "cd $staging_dist_dir" +cd $staging_dist_dir +echo "rm tree.json manifest.json cdk.out" +rm tree.json manifest.json cdk.out + +echo "------------------------------------------------------------------------------" +echo "[Packing] Template artifacts" +echo "------------------------------------------------------------------------------" + +# Move outputs from staging to template_dist_dir +echo "Move outputs from staging to template_dist_dir" +echo "cp $template_dir/*.template $template_dist_dir/" +cp $staging_dist_dir/*.template.json $template_dist_dir/ +rm *.template.json + +# Rename all *.template.json files to *.template +echo "Rename all *.template.json to *.template" +echo "copy templates and rename" +for f in $template_dist_dir/*.template.json; do + mv -- "$f" "${f%.template.json}.template" +done + +echo "------------------------------------------------------------------------------" +echo "[Packing] Source code artifacts" +echo "------------------------------------------------------------------------------" + +# General cleanup of node_modules and package-lock.json files +echo "find $staging_dist_dir -iname "node_modules" -type d -exec rm -rf "{}" \; 2> /dev/null" +find $staging_dist_dir -iname "node_modules" -type d -exec rm -rf "{}" \; 2> /dev/null +echo "find $staging_dist_dir -iname "package-lock.json" -type f -exec rm -f "{}" \; 2> /dev/null" +find $staging_dist_dir -iname "package-lock.json" -type f -exec rm -f "{}" \; 2> /dev/null + +echo "------------------------------------------------------------------------------" +echo "Package Network Firewall Automation node project for Code Build/Deploy stage " +echo "------------------------------------------------------------------------------" +cd $source_dir/networkFirewallAutomation/ +npm install +npm run build +npm run zip +if [ "$?" = "1" ]; then + echo "(npm run zip) ERROR: there is likely output above." 1>&2 + exit 1 +fi +echo "Copy package zip to dist directory" +echo "cp ./dist/network-firewall-automation.zip $build_dist_dir/network-firewall-automation.zip" +cp ./dist/network-firewall-automation.zip $build_dist_dir/network-firewall-automation.zip + +# build regional rule groups zip files for each region +echo "Copying network firewall configurations to deployment folder" +cd $template_dir +cp -pr $source_dir/networkFirewallAutomation/config/* ./ +echo -e "\n Creating a zip file with network firewall configurations" +echo -e "\n Building network firewall configuration" +zip -Xr "$build_dist_dir"/network-firewall-configuration.zip ./firewalls ./ruleGroups ./firewallPolicies ./examples + +echo "------------------------------------------------------------------------------" +echo "[Cleanup] Remove temporary files" +echo "------------------------------------------------------------------------------" + +# Delete the temporary /staging folder +echo "rm -rf $staging_dist_dir" +rm -rf $staging_dist_dir +rm -rf ./ruleGroups +rm -rf ./firewallPolicies +rm -rf ./firewalls diff --git a/source/architecture.png b/source/architecture.png new file mode 100644 index 0000000..76158f9 Binary files /dev/null and b/source/architecture.png differ diff --git a/source/bin/network-firewall-auto-solution.ts b/source/bin/network-firewall-auto-solution.ts new file mode 100755 index 0000000..68067fa --- /dev/null +++ b/source/bin/network-firewall-auto-solution.ts @@ -0,0 +1,40 @@ +#!/usr/bin/env node +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +import * as cdk from '@aws-cdk/core'; +import { + NetworkFirewallAutomationStack, + NetworkFirewallAutomationStackProps +} from '../lib/network-firewall-automation-solution-stack'; + +const SOLUTION_VERSION = process.env['DIST_VERSION']; +const SOLUTION_NAME = process.env['SOLUTION_NAME']; +const SOLUTION_ID = process.env['SOLUTION_ID'] || 'SO0108'; +const SOLUTION_BUCKET = process.env['DIST_OUTPUT_BUCKET']; +const SOLUTION_TMN = process.env['SOLUTION_TRADEMARKEDNAME']; +const SOLUTION_PROVIDER = 'AWS Solution Development'; + +const app = new cdk.App(); + +let NetworkFirewallAutomationStackProperties: NetworkFirewallAutomationStackProps = { + solutionId: SOLUTION_ID, + solutionTradeMarkName: SOLUTION_TMN, + solutionProvider: SOLUTION_PROVIDER, + solutionBucket: SOLUTION_BUCKET, + solutionName: SOLUTION_NAME, + solutionVersion: SOLUTION_VERSION, + description: '(' + SOLUTION_ID + ') - The AWS CloudFormation template' + + ' for deployment of the ' + SOLUTION_NAME + ', Version: ' + SOLUTION_VERSION, +} + +new NetworkFirewallAutomationStack(app, 'aws-network-firewall-deployment-automations-for-aws-transit-gateway', NetworkFirewallAutomationStackProperties); diff --git a/source/cdk.json b/source/cdk.json new file mode 100755 index 0000000..9a31f98 --- /dev/null +++ b/source/cdk.json @@ -0,0 +1,3 @@ +{ + "app": "npx ts-node bin/network-firewall-auto-solution.ts" +} diff --git a/source/lib/network-firewall-automation-solution-stack.ts b/source/lib/network-firewall-automation-solution-stack.ts new file mode 100755 index 0000000..7e5c5f3 --- /dev/null +++ b/source/lib/network-firewall-automation-solution-stack.ts @@ -0,0 +1,1222 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import * as cdk from '@aws-cdk/core'; +import { RemovalPolicy } from '@aws-cdk/core'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as s3 from '@aws-cdk/aws-s3'; +import * as logs from '@aws-cdk/aws-logs'; +import * as iam from '@aws-cdk/aws-iam'; +import * as kms from '@aws-cdk/aws-kms'; +import * as codecommit from '@aws-cdk/aws-codecommit'; +import * as codepipeline from '@aws-cdk/aws-codepipeline'; +import * as codepipeline_action from '@aws-cdk/aws-codepipeline-actions'; +import { + BuildEnvironmentVariableType, + BuildSpec, + LinuxBuildImage, + PipelineProject +} from '@aws-cdk/aws-codebuild'; + + +export interface NetworkFirewallAutomationStackProps extends cdk.StackProps { + solutionId: string; + solutionTradeMarkName: string | undefined; + solutionProvider: string | undefined; + solutionBucket: string | undefined; + solutionName: string | undefined; + solutionVersion: string | undefined; +} + +export class NetworkFirewallAutomationStack extends cdk.Stack { + + constructor(scope: cdk.Construct, id: string, props: NetworkFirewallAutomationStackProps) { + super(scope, id, props); + + /** + * Parameters - Values to pass to your template at runtime + */ + + const cidrBlock = new cdk.CfnParameter(this, 'cidrBlock', { + type: 'String', + default: '192.168.1.0/26', + description: 'CIDR Block for VPC. Must be /26 or larger CIDR block.', + allowedPattern: '^(?:[0-9]{1,3}\.){3}[0-9]{1,3}[\/]([0-9]?[0-6]?|[1][7-9])$' + }) + + const logRetentionPeriod = new cdk.CfnParameter(this, "LogRetentionPeriod", { + type: "Number", + description: "Log retention period in days.", + allowedValues: ["1", "3", "5", "7", "14", "30", "60", "90", "120", "150", "180", "365", "400", "545", "731", "1827", "3653"], + default: 90 + }); + + const existingTransitGatewayId = new cdk.CfnParameter(this, "ExistingTransitGateway", { + description: 'Existing AWS Transit Gateway id.', + type: 'String', + default: "" + }) + + const transitGatewayRTIdForAssociation = new cdk.CfnParameter(this, "TransitGatewayRouteTableIdForAssociation", { + description: 'Existing AWS Transit Gateway route table id. Example:' + + ' Firewall Route Table. Format: tgw-rtb-0a1b2c3d', + type: 'String', + default: "" + }) + + const transitGatewayRTIdForDefaultRoute = new cdk.CfnParameter(this, "TransitGatewayRTIdForDefaultRoute", { + description: 'Existing AWS Transit Gateway route table id.' + + ' Example: Spoke VPC Route Table. Format: tgw-rtb-4e5f6g7h', + type: 'String', + default: "" + }) + + const logType = new cdk.CfnParameter(this, "logType", { + type: "String", + description: 'The type of log to send. Alert logs report traffic that' + + ' matches a StatefulRule with an action setting that sends an alert' + + ' log message. Flow logs are standard network traffic flow logs.', + allowedValues: ['ALERT', 'FLOW', 'EnableBoth'], + default: 'FLOW', + }) + + const logDestinationType = new cdk.CfnParameter(this, "logDestinationType", { + type: "String", + description: 'The type of storage destination to send these logs to.' + + ' You can send logs to an Amazon S3 bucket ' + + 'or a CloudWatch log group.', + allowedValues: ['S3', 'CloudWatchLogs', 'ConfigureManually'], + default: 'CloudWatchLogs', + }) + + /** + * Metadata - Objects that provide additional information about the + * template. + */ + + this.templateOptions.metadata = { + "AWS::CloudFormation::Interface": { + ParameterGroups: [ + { + Label: { default: "VPC Configuration" }, + Parameters: [cidrBlock.logicalId] + }, + { + Label: { default: "Transit Gateway Configuration" }, + Parameters: [ + existingTransitGatewayId.logicalId, + transitGatewayRTIdForAssociation.logicalId, + transitGatewayRTIdForDefaultRoute.logicalId + ] + }, + { + Label: { default: "Firewall Logging Configuration" }, + Parameters: [ + logDestinationType.logicalId, + logType.logicalId, + logRetentionPeriod.logicalId + ] + } + ], + ParameterLabels: { + [cidrBlock.logicalId]: { + default: "Provide the CIDR block for the Inspection VPC", + }, + [existingTransitGatewayId.logicalId]: { + default: "Provide the existing AWS Transit Gateway ID you wish to" + + " attach to the Inspection VPC", + }, + [transitGatewayRTIdForAssociation.logicalId]: { + default: "Provide AWS Transit Gateway Route Table to be" + + " associated with the Inspection VPC TGW Attachment.", + }, + [transitGatewayRTIdForDefaultRoute.logicalId]: { + default: "Provide the AWS Transit Gateway Route Table to receive 0.0.0.0/0 route to the Inspection VPC TGW Attachment.", + }, + [logType.logicalId]: { + default: "Select the type of log to send to the defined log" + + " destination.", + }, + [logDestinationType.logicalId]: { + default: "Select the type of log destination for the Network" + + " Firewall", + }, + [logRetentionPeriod.logicalId]: { + default: "Select the log retention period for Network Firewall" + + " Logs.", + } + }, + }, + }; + + /** + * Mappings - define fixed values + */ + const mappings = new cdk.CfnMapping(this, 'SolutionMapping') + mappings.setValue('Version', 'Latest', 'latest') + mappings.setValue('Route', 'QuadZero', '0.0.0.0/0') + mappings.setValue('Log', 'Level', 'info') + mappings.setValue('CodeCommitRepo', 'Name', 'network-firewall-config-repo-') + mappings.setValue('Metrics', 'URL', 'https://metrics.awssolutionsbuilder.com/generic') + mappings.setValue('Solution', 'Identifier', 'SO0108') + mappings.setValue('TransitGatewayAttachment', 'ApplianceMode', 'enable') + + const send = new cdk.CfnMapping(this, 'Send') + send.setValue('AnonymousUsage', 'Data', 'Yes') + send.setValue('ParameterKey', 'UniqueId', `/Solutions/${props.solutionName}/UUID`) + + + /** + * Conditions - control whether certain resources are created or whether + * certain resource properties are assigned a value during stack + * creation or update. + */ + + const isLoggingInS3 = new cdk.CfnCondition(this, + "LoggingInS3", + { + expression: cdk.Fn.conditionEquals(logDestinationType.valueAsString, 'S3') + }) + + const isLoggingInCloudWatch = new cdk.CfnCondition(this, + "LoggingInCloudWatch", + { + expression: cdk.Fn.conditionEquals(logDestinationType.valueAsString, 'CloudWatchLogs') + }) + + const isNotLoggingConfigureManually = new cdk.CfnCondition(this, + "NotLoggingConfigureManually", + { + expression: cdk.Fn.conditionNot(cdk.Fn.conditionEquals(logDestinationType.valueAsString, 'ConfigureManually')) + }) + + /** + * condition to determine if transit gateway id is provided or not if + * provided use it to create transit gateway attachment else skip + */ + + const createTransitGatewayAttachment = new cdk.CfnCondition(this, + "CreateTransitGatewayAttachment", + { + expression: cdk.Fn.conditionNot(cdk.Fn.conditionEquals(existingTransitGatewayId.valueAsString, '')) + }) + + /** + * condition to determine if transit gateway route table id is provided or + * not. if provided use it to create route table association else skip + */ + const createTransitGatewayRTAssociation = new cdk.CfnCondition(this, + "CreateTransitGatewayRTAssociation", + { + expression: cdk.Fn.conditionAnd( + cdk.Fn.conditionNot( + cdk.Fn.conditionEquals( + transitGatewayRTIdForAssociation.valueAsString, '')), createTransitGatewayAttachment) + }) + + /** + * condition to determine if transit gateway route table id is provided or + * not. if provided use it to create route table propagation else skip + */ + const createDefaultRouteFirewallRT = new cdk.CfnCondition(this, + "CreateDefaultRouteFirewallRT", + { + expression: cdk.Fn.conditionAnd( + cdk.Fn.conditionNot( + cdk.Fn.conditionEquals( + transitGatewayRTIdForDefaultRoute.valueAsString, '')), createTransitGatewayAttachment) + }) + + /** + * Resources - Specifies the stack resources and their properties + */ + + this.templateOptions.templateFormatVersion = '2010-09-09'; + + // Create a new VPC + + const vpc = new ec2.CfnVPC(this, 'VPC', { + cidrBlock: cidrBlock.valueAsString, + }); + + //KMS Key for the VPC Flow logs and Firewall Logs + const KMSKeyForNetworkFirewallLogDestinations = new kms.Key(this, "KMSKeyForNetworkFirewallLogDestinations", { + description: "This key will be used for encrypting the vpc flow logs and firewall logs.", + enableKeyRotation: true + }) + + //Permissions for network firewall service to be able use this key for publishing logs to S3. + KMSKeyForNetworkFirewallLogDestinations.addToResourcePolicy(new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + resources: ["*"], + principals: [new iam.ServicePrincipal("delivery.logs.amazonaws.com")], + actions: ["kms:GenerateDataKey*"] + })) + //Permissions for network firewall service to be able use this key for publishing logs to cloudwatch. + KMSKeyForNetworkFirewallLogDestinations.addToResourcePolicy(new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + resources: ["*"], + actions: [ + "kms:Encrypt*", + "kms:Decrypt*", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Describe*" + ], + principals: [ + new iam.ServicePrincipal(`logs.${cdk.Aws.REGION}.amazonaws.com`) + ] + })) + + // Create a new log group for Firewall logging + const cloudWatchLogGroup = new logs.CfnLogGroup(this, 'CloudWatchLogGroup', { + retentionInDays: logRetentionPeriod.valueAsNumber, + kmsKeyId: KMSKeyForNetworkFirewallLogDestinations.keyArn + }) + + cloudWatchLogGroup.cfnOptions.condition = isLoggingInCloudWatch; + + const logsBucket = new s3.Bucket(this, 'Logs', { + encryption: s3.BucketEncryption.KMS, + encryptionKey: KMSKeyForNetworkFirewallLogDestinations, + publicReadAccess: false, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + lifecycleRules: [{ + expiration: cdk.Duration.days(logRetentionPeriod.valueAsNumber) + }] + }); + + const cfnLogsBucket = logsBucket.node.defaultChild as s3.CfnBucket; + cfnLogsBucket.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [{ + id: 'W35', + reason: 'Logs bucket does not require logging configuration' + }, { + id: 'W51', + reason: 'Logs bucket is private and does not require a bucket policy' + }] + } + }; + cfnLogsBucket.cfnOptions.condition = isLoggingInS3; + + //Solution Logging Changes stop. + + + vpc.applyRemovalPolicy(RemovalPolicy.RETAIN) + vpc.tags.setTag('Name', `${cdk.Aws.STACK_NAME}-Inspection-VPC`) + vpc.tags.setTag('created-by', `${props.solutionName}`) + + const cidrCount = 4 + const cidrBits = '4' + const availabilityZoneA = { + "Fn::Select": [ + "0", + { + "Fn::GetAZs": "" + } + ] + } + const availabilityZoneB = { + "Fn::Select": [ + "1", + { + "Fn::GetAZs": "" + } + ] + } + + // Create Firewall Subnet 1 + const NetworkFirewallSubnet1 = new ec2.CfnSubnet(this, "NetworkFirewallSubnet1", { + vpcId: vpc.ref, + cidrBlock: cdk.Fn.select( + 0, + cdk.Fn.cidr( + vpc.attrCidrBlock, + cidrCount, + cidrBits + ) + ) + }) + NetworkFirewallSubnet1.tags.setTag("Name", `${cdk.Aws.STACK_NAME}-FirewallSubnet1`) + NetworkFirewallSubnet1.applyRemovalPolicy(RemovalPolicy.RETAIN) + NetworkFirewallSubnet1.addPropertyOverride('AvailabilityZone', availabilityZoneA) + + + // Create Firewall Subnet 2 + const NetworkFirewallSubnet2 = new ec2.CfnSubnet(this, "NetworkFirewallSubnet2", { + vpcId: vpc.ref, + cidrBlock: cdk.Fn.select( + 1, + cdk.Fn.cidr( + vpc.attrCidrBlock, + cidrCount, + cidrBits + ) + ) + }) + + NetworkFirewallSubnet2.tags.setTag("Name", `${cdk.Aws.STACK_NAME}-FirewallSubnet2`) + NetworkFirewallSubnet2.applyRemovalPolicy(RemovalPolicy.RETAIN) + NetworkFirewallSubnet2.addPropertyOverride('AvailabilityZone', availabilityZoneB) + + //Subnet Route Tables. + const firewallSubnetRouteTable = new ec2.CfnRouteTable(this, "FirewallSubnetRouteTable", { + vpcId: vpc.ref + }) + firewallSubnetRouteTable.tags.setTag("Name", `${cdk.Aws.STACK_NAME}-FirewallSubnetRouteTable`) + firewallSubnetRouteTable.applyRemovalPolicy(RemovalPolicy.RETAIN) + + //Subnet Route Table Associations. + const NetworkFirewallSubnet1RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation(this, "NetworkFirewallSubnet1RouteTableAssociation", { + subnetId: NetworkFirewallSubnet1.ref, + routeTableId: firewallSubnetRouteTable.ref + }) + NetworkFirewallSubnet1RouteTableAssociation.applyRemovalPolicy(RemovalPolicy.RETAIN) + + const NetworkFirewallSubnet2RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation(this, "NetworkFirewallSubnet2RouteTableAssociation", { + subnetId: NetworkFirewallSubnet2.ref, + routeTableId: firewallSubnetRouteTable.ref + }) + NetworkFirewallSubnet2RouteTableAssociation.applyRemovalPolicy(RemovalPolicy.RETAIN) + + // Create Transit Gateway Subnet 1 + const vpcTGWSubnet1 = new ec2.CfnSubnet(this, "VPCTGWSubnet1", { + vpcId: vpc.ref, + cidrBlock: cdk.Fn.select( + 2, + cdk.Fn.cidr( + vpc.attrCidrBlock, + cidrCount, + cidrBits + ) + ) + }) + vpcTGWSubnet1.tags.setTag("Name", `${cdk.Aws.STACK_NAME}-VPCTGWSubnet1`) + vpcTGWSubnet1.applyRemovalPolicy(RemovalPolicy.RETAIN) + vpcTGWSubnet1.addPropertyOverride('AvailabilityZone', availabilityZoneA) + + // Create Transit Gateway Subnet 2 + const vpcTGWSubnet2 = new ec2.CfnSubnet(this, "VPCTGWSubnet2", { + vpcId: vpc.ref, + cidrBlock: cdk.Fn.select( + 3, + cdk.Fn.cidr( + vpc.attrCidrBlock, + cidrCount, + cidrBits + ) + ) + }) + vpcTGWSubnet2.tags.setTag("Name", `${cdk.Aws.STACK_NAME}-VPCTGWSubnet2`) + vpcTGWSubnet2.applyRemovalPolicy(RemovalPolicy.RETAIN) + vpcTGWSubnet2.addPropertyOverride('AvailabilityZone', availabilityZoneB) + + //Route Tables for VPC Transit Gateway subnets. + const vpcTGWRouteTable1 = new ec2.CfnRouteTable(this, "VPCTGWRouteTable1", { + vpcId: vpc.ref + }) + vpcTGWRouteTable1.tags.setTag("Name", `${cdk.Aws.STACK_NAME}-TGWSubnetRouteTable1`) + vpcTGWRouteTable1.applyRemovalPolicy(RemovalPolicy.RETAIN) + + const vpcTGWRouteTable2 = new ec2.CfnRouteTable(this, "VPCTGWRouteTable2", { + vpcId: vpc.ref + }) + vpcTGWRouteTable2.tags.setTag("Name", `${cdk.Aws.STACK_NAME}-TGWSubnetRouteTable2`) + vpcTGWRouteTable2.applyRemovalPolicy(RemovalPolicy.RETAIN) + + //Subnet Route Table Associations for Transit Gateway Subnets + const vpcTGWSubnet1RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation(this, "VPCTGWSubnet1RouteTableAssociation", { + subnetId: vpcTGWSubnet1.ref, + routeTableId: vpcTGWRouteTable1.ref + }) + vpcTGWSubnet1RouteTableAssociation.applyRemovalPolicy(RemovalPolicy.RETAIN) + + const vpcTGWSubnet2RouteTableAssociation = new ec2.CfnSubnetRouteTableAssociation(this, "VPCTGWSubnet2RouteTableAssociation", { + subnetId: vpcTGWSubnet2.ref, + routeTableId: vpcTGWRouteTable2.ref, + }) + vpcTGWSubnet2RouteTableAssociation.applyRemovalPolicy(RemovalPolicy.RETAIN) + + //VPC Flow Log + const logGroup = new logs.CfnLogGroup(this, "LogGroupFlowLogs", { + retentionInDays: logRetentionPeriod.valueAsNumber, + logGroupName: cdk.Aws.STACK_NAME, + kmsKeyId: KMSKeyForNetworkFirewallLogDestinations.keyArn + }) + + const flowLogRole = new iam.Role(this, "RoleFlowLogs", { + assumedBy: new iam.ServicePrincipal("vpc-flow-logs.amazonaws.com") + }); + + const policyStatement = new iam.PolicyStatement({ + actions: [ + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:PutLogEvents", + "logs:CreateLogGroup", + "logs:DescribeLogGroups"], + resources: [logGroup.attrArn] + }); + policyStatement.effect = iam.Effect.ALLOW; + flowLogRole.addToPolicy(policyStatement); + + + new ec2.CfnFlowLog(this, "FlowLog", { + deliverLogsPermissionArn: flowLogRole.roleArn, + logGroupName: logGroup.logGroupName, + resourceId: vpc.ref, + resourceType: "VPC", + trafficType: "ALL" + }); + + //Start: associate for an existing transit gateway if user provides one. + + + //Transit gateway attachment. + const vpcTGWAttachment = new ec2.CfnTransitGatewayAttachment(this, 'VPC_TGW_ATTACHMENT', { + transitGatewayId: existingTransitGatewayId.valueAsString, + vpcId: vpc.ref, + subnetIds: [ + vpcTGWSubnet1.ref, + vpcTGWSubnet2.ref + ] + }) + vpcTGWAttachment.cfnOptions.condition = createTransitGatewayAttachment + vpcTGWAttachment.tags.setTag('Name', `${cdk.Aws.STACK_NAME}-Inspection-VPC-Attachment`) + vpcTGWAttachment.applyRemovalPolicy(RemovalPolicy.RETAIN) + vpcTGWAttachment.addDeletionOverride("UpdateReplacePolicy") + + //add the transit gateway id provided by the user to the firewall route + // table created for transit gateway interaction. + const defaultTransitGatewayRoute = new ec2.CfnRoute(this, 'TGWRoute', { + routeTableId: firewallSubnetRouteTable.ref, + destinationCidrBlock: mappings.findInMap('Route', 'QuadZero'), + transitGatewayId: existingTransitGatewayId.valueAsString + }) + defaultTransitGatewayRoute.cfnOptions.condition = createTransitGatewayAttachment + defaultTransitGatewayRoute.addDependsOn(vpcTGWAttachment) + + + //Transit Gateway association with the TGW route table id provided by the user. + const tgwRouteTableAssociation = new ec2.CfnTransitGatewayRouteTableAssociation(this, 'VPCTGWRouteTableAssociation', { + transitGatewayAttachmentId: vpcTGWAttachment.ref, + transitGatewayRouteTableId: transitGatewayRTIdForAssociation.valueAsString + }) + + //createTransitGatewayRTAssociation + tgwRouteTableAssociation.cfnOptions.condition = createTransitGatewayRTAssociation + tgwRouteTableAssociation.addOverride("DeletionPolicy", "Retain") + tgwRouteTableAssociation.addDeletionOverride("UpdateReplacePolicy") + + // Add default route to Instection VPC-TGW Attachment in the Spoke VPC + // Route Transit Gateway Route Table + const defaultRouteSpokeVPCTGWRouteTable = new ec2.CfnTransitGatewayRoute(this, 'DefaultRouteSpokeVPCTGWRouteTable', { + transitGatewayRouteTableId: transitGatewayRTIdForDefaultRoute.valueAsString, + destinationCidrBlock: mappings.findInMap('Route', 'QuadZero'), + transitGatewayAttachmentId: vpcTGWAttachment.ref + }) + defaultRouteSpokeVPCTGWRouteTable.cfnOptions.condition = createDefaultRouteFirewallRT + defaultRouteSpokeVPCTGWRouteTable.addOverride("DeletionPolicy", "Retain") + + //End: Transit gateway changes. + + //CodeCommit Repo and Code Pipeline with default policy created. + const codeCommitRepo = new codecommit.Repository(this, 'NetworkFirewallCodeRepository', { + repositoryName: mappings.findInMap("CodeCommitRepo", "Name") + cdk.Aws.STACK_NAME, + description: 'This repository is created by the AWS Network Firewall' + + ' solution for AWS Transit Gateway, to store and trigger changes to' + + ' the network firewall rules and configurations.' + }) + + const codeCommitRepo_cfn_ref = codeCommitRepo.node.defaultChild as codecommit.CfnRepository + codeCommitRepo_cfn_ref.addOverride("Properties.Code.S3.Bucket", `${props.solutionBucket}-${this.region}`) + codeCommitRepo_cfn_ref.addOverride("Properties.Code.S3.Key", `${props.solutionName}/${mappings.findInMap('Version', 'Latest')}/network-firewall-configuration.zip`) + codeCommitRepo_cfn_ref.addOverride("DeletionPolicy", "Retain") + codeCommitRepo_cfn_ref.addOverride("UpdateReplacePolicy", "Retain") + + const codeBuildStagesSourceCodeBucket = new s3.Bucket(this, 'CodeBuildStagesSourceCodeBucket', { + publicReadAccess: false, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL + }); + + const sourceOutputArtifact = new codepipeline.Artifact('SourceArtifact') + const buildOutputArtifact = new codepipeline.Artifact('BuildArtifact') + + const subnetIds = NetworkFirewallSubnet1.ref + ',' + NetworkFirewallSubnet2.ref + const codeBuildEnvVariables = { + ['LOG_LEVEL']: + { + value: mappings.findInMap('Log', 'Level'), + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['VPC_ID']: + { + value: vpc.ref, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['SUBNET_IDS']: + { + value: subnetIds, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['LOG_TYPE']: + { + value: logType.valueAsString, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['LOG_DESTINATION_TYPE']: + { + value: logDestinationType.valueAsString, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['S3_LOG_BUCKET_NAME']: + { + value: cdk.Fn.conditionIf('LoggingInS3', logsBucket.bucketName, 'NotConfigured'), + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['CLOUDWATCH_LOG_GROUP_NAME']: + { + value: cdk.Fn.conditionIf('LoggingInCloudWatch', cloudWatchLogGroup.ref, 'NotConfigured'), + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['VPC_TGW_ATTACHMENT_AZ_1']: + { + value: cdk.Fn.getAtt( + 'NetworkFirewallSubnet1', + 'AvailabilityZone').toString(), + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['VPC_TGW_ATTACHMENT_AZ_2']: + { + value: cdk.Fn.getAtt( + 'NetworkFirewallSubnet2', + 'AvailabilityZone').toString(), + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['VPC_TGW_ATTACHMENT_ROUTE_TABLE_ID_1']: + { + value: vpcTGWRouteTable1.ref, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['VPC_TGW_ATTACHMENT_ROUTE_TABLE_ID_2']: + { + value: vpcTGWRouteTable2.ref, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['CODE_BUILD_SOURCE_CODE_S3_KEY']: { + value: `${props.solutionName}/${props.solutionVersion}`, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['STACK_ID']: { + value: `${cdk.Aws.STACK_ID}`, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['SSM_PARAM_FOR_UUID']: { + value: send.findInMap('ParameterKey', 'UniqueId'), + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['SEND_ANONYMOUS_METRICS']: { + value: `${send.findInMap('AnonymousUsage', 'Data')}`, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['SOLUTION_ID']: { + value: `${mappings.findInMap('Solution', 'Identifier')}`, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['METRICS_URL']: { + value: `${mappings.findInMap('Metrics', 'URL')}`, + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['TRANSIT_GATEWAY_ATTACHMENT_ID']: { + value: cdk.Fn.conditionIf(createTransitGatewayAttachment.logicalId, vpcTGWAttachment.ref, ''), + type: BuildEnvironmentVariableType.PLAINTEXT + }, + ['TRANSIT_GATEWAY_ATTACHMENT_APPLIANCE_MODE']: { + value: mappings.findInMap('TransitGatewayAttachment', 'ApplianceMode'), + type: BuildEnvironmentVariableType.PLAINTEXT + } + } + + // Code build project, code build role will be created by the construct. + const buildProject = new PipelineProject(this, 'BuildProject', { + buildSpec: BuildSpec.fromObject({ + version: '0.2', + phases: { + install: { + 'runtime-versions': { + nodejs: '12' + }, + commands: [`export current=$(pwd)`, `export sourceCodeKey=$CODE_BUILD_SOURCE_CODE_S3_KEY`] + }, + pre_build: { + commands: [ + `cd $current`, + `pwd; ls -ltr`, + `echo 'Download Network Firewall Solution Package'`, + `aws s3 cp s3://${codeBuildStagesSourceCodeBucket.bucketName}/$sourceCodeKey/network-firewall-automation.zip $current || true`, + `if [ -f $current/network-firewall-automation.zip ];then exit 0;else echo \"Copy file to s3 bucket\"; aws s3 cp s3://${props.solutionBucket}-${cdk.Aws.REGION}/$sourceCodeKey/network-firewall-automation.zip s3://${codeBuildStagesSourceCodeBucket.bucketName}/$sourceCodeKey/network-firewall-automation.zip; aws s3 cp s3://${codeBuildStagesSourceCodeBucket.bucketName}/$sourceCodeKey/network-firewall-automation.zip $current; fi;`, + `unzip -o $current/network-firewall-automation.zip -d $current`, + `pwd; ls -ltr`, + ] + }, + build: { + commands: [ + `echo "Validating the firewall config"`, + `node build.js` + ] + } + }, + artifacts: { + files: "**/*" + } + }), + environment: { + buildImage: LinuxBuildImage.STANDARD_4_0 + }, + environmentVariables: codeBuildEnvVariables + }) + + const buildStageIAMPolicy = new iam.Policy(this, 'buildStageIAMPolicy', { + statements: [ + new iam.PolicyStatement({ + actions: [ + "network-firewall:CreateFirewallPolicy", + "network-firewall:CreateRuleGroup" + ], + resources: [ + cdk.Fn.sub("arn:${AWS::Partition}:network-firewall:${AWS::Region}:${AWS::AccountId}:stateful-rulegroup/*"), + cdk.Fn.sub("arn:${AWS::Partition}:network-firewall:${AWS::Region}:${AWS::AccountId}:firewall-policy/*"), + cdk.Fn.sub("arn:${AWS::Partition}:network-firewall:${AWS::Region}:${AWS::AccountId}:stateless-rulegroup/*") + ], + effect: iam.Effect.ALLOW + }), + new iam.PolicyStatement({ + actions: ["s3:GetObject"], + resources: [cdk.Fn.sub("arn:${AWS::Partition}:s3:::${CodeBucketName}/${KeyName}/*", { + CodeBucketName: `${props.solutionBucket}-${this.region}`, + KeyName: `${props.solutionName}` + }), + `arn:${cdk.Aws.PARTITION}:s3:::${codeBuildStagesSourceCodeBucket.bucketName}/*`] + }), + new iam.PolicyStatement({ + actions: ["s3:PutObject"], + resources: [ + `arn:${cdk.Aws.PARTITION}:s3:::${codeBuildStagesSourceCodeBucket.bucketName}/*` + ], + effect: iam.Effect.ALLOW + }), + new iam.PolicyStatement({ + actions: [ + "ssm:PutParameter", + "ssm:GetParameter", + ], + effect: iam.Effect.ALLOW, + resources: [ + cdk.Fn.sub("arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${ParameterKey}", { + ParameterKey: `${send.findInMap('ParameterKey', 'UniqueId')}` + }) + ] + }), + ] + }) + + buildProject.role?.attachInlinePolicy(buildStageIAMPolicy) + + //IAM Policy and Role to execute deploy stage + + const deployStageFirewallPolicy = new iam.Policy(this, + 'deployStageFirewallPolicy', + { + statements: [ + new iam.PolicyStatement({ + actions: [ + "network-firewall:CreateFirewall", + "network-firewall:UpdateFirewallDeleteProtection", + "network-firewall:DeleteRuleGroup", + "network-firewall:DescribeLoggingConfiguration", + "network-firewall:UpdateFirewallDescription", + "network-firewall:CreateRuleGroup", + "network-firewall:DescribeFirewall", + "network-firewall:DeleteFirewallPolicy", + "network-firewall:UpdateRuleGroup", + "network-firewall:DescribeRuleGroup", + "network-firewall:ListRuleGroups", + "network-firewall:UpdateSubnetChangeProtection", + "network-firewall:UpdateFirewallPolicyChangeProtection", + "network-firewall:AssociateFirewallPolicy", + "network-firewall:DescribeFirewallPolicy", + "network-firewall:UpdateFirewallPolicy", + "network-firewall:DescribeResourcePolicy", + "network-firewall:CreateFirewallPolicy", + "network-firewall:UpdateLoggingConfiguration", + "network-firewall:TagResource" + ], + resources: [ + cdk.Fn.sub("arn:${AWS::Partition}:network-firewall:${AWS::Region}:${AWS::AccountId}:stateful-rulegroup/*"), + cdk.Fn.sub("arn:${AWS::Partition}:network-firewall:${AWS::Region}:${AWS::AccountId}:firewall-policy/*"), + cdk.Fn.sub("arn:${AWS::Partition}:network-firewall:${AWS::Region}:${AWS::AccountId}:firewall/*"), + cdk.Fn.sub("arn:${AWS::Partition}:network-firewall:${AWS::Region}:${AWS::AccountId}:stateless-rulegroup/*") + ] + }), + new iam.PolicyStatement({ + actions: ["s3:GetObject"], + resources: [cdk.Fn.sub("arn:${AWS::Partition}:s3:::${CodeBucketName}/${KeyName}/*", { + CodeBucketName: `${props.solutionBucket}-${this.region}`, + KeyName: `${props.solutionName}` + }), + `arn:${cdk.Aws.PARTITION}:s3:::${codeBuildStagesSourceCodeBucket.bucketName}/*`] + }), + new iam.PolicyStatement({ + actions: [ + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeRouteTables" + ], + resources: ["*"] + }), + new iam.PolicyStatement({ + actions: [ + "ec2:CreateRoute", + "ec2:DeleteRoute", + ], + effect: iam.Effect.ALLOW, + resources: [ + `arn:${cdk.Aws.PARTITION}:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:route-table/${vpcTGWRouteTable1.ref}`, + `arn:${cdk.Aws.PARTITION}:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:route-table/${vpcTGWRouteTable2.ref}` + ] + }), + new iam.PolicyStatement({ + actions: ["iam:CreateServiceLinkedRole"], + resources: [cdk.Fn.sub("arn:aws:iam::${AWS::AccountId}:role/aws-service-role/network-firewall.amazonaws.com/AWSServiceRoleForNetworkFirewall")] + }) + ] + }) + + const deployStageFirewallPolicyResource = deployStageFirewallPolicy.node.findChild('Resource') as iam.CfnPolicy; + + deployStageFirewallPolicyResource.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W12', + reason: 'Resource * is required for describe APIs' + }] + } + }; + + //add modify transit gateway attachement permission only if the transit gateway attachment is provided. + const deployStageModifyTransitGatewayAttachmentPolicy = new iam.Policy(this, 'deployStageModifyTransitGatewayAttachmentPolicy', { + statements: [ + new iam.PolicyStatement({ + actions: [ + "ec2:ModifyTransitGatewayVpcAttachment" + ], + effect: iam.Effect.ALLOW, + resources: [ + `arn:${cdk.Aws.PARTITION}:ec2:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:transit-gateway-attachment/${vpcTGWAttachment.ref}`, + ] + }) + ] + }) + const resourcePolicyModifyTGWAttachment = deployStageModifyTransitGatewayAttachmentPolicy.node.findChild('Resource') as iam.CfnPolicy; + resourcePolicyModifyTGWAttachment.cfnOptions.condition = createTransitGatewayAttachment + + const deployStageFirewallLoggingPolicy = new iam.Policy(this, + 'deployStageFirewallLoggingPolicy', + { + statements: [ + new iam.PolicyStatement({ + actions: [ + "logs:CreateLogDelivery", + "logs:GetLogDelivery", + "logs:UpdateLogDelivery", + "logs:DeleteLogDelivery", + "logs:ListLogDeliveries" + ], + resources: ["*"] // Per IAM service must use All Resources + }) + ] + }) + + const deployStageFirewallLoggingResource = deployStageFirewallLoggingPolicy.node.findChild('Resource') as iam.CfnPolicy; + + deployStageFirewallLoggingResource.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W12', + reason: 'Resource * is required for these actions.' + }] + } + }; + + // skip creating the 'deployStageFirewallLoggingPolicy' IAM policy if + // logging destination type is set to configure manually + deployStageFirewallLoggingResource.cfnOptions.condition = isNotLoggingConfigureManually + + const deployStageFirewallLoggingS3Policy = new iam.Policy(this, + 'deployStageFirewallLoggingS3Policy', + { + statements: [ + new iam.PolicyStatement({ + actions: [ + "s3:PutBucketPolicy", + "s3:GetBucketPolicy" + ], + resources: [logsBucket.bucketArn] + }) + ] + }) + + const deployStageFirewallLoggingS3PolicyResource = deployStageFirewallLoggingS3Policy.node.findChild('Resource') as iam.CfnPolicy; + + // create the 'deployStageFirewallLoggingS3Policy' IAM policy only if + // logging destination type is set to S3 + deployStageFirewallLoggingS3PolicyResource.cfnOptions.condition = isLoggingInS3 + + const deployStageFirewallLoggingCWPolicy = new iam.Policy(this, + 'deployStageFirewallLoggingCWPolicy', + { + statements: [ + new iam.PolicyStatement({ + actions: [ + "logs:PutResourcePolicy", + "logs:DescribeResourcePolicies" + ], + resources: ["*"] // Per IAM service must use All Resources + }), + new iam.PolicyStatement({ + actions: [ + "logs:DescribeLogGroups" + ], + resources: [ + cdk.Fn.sub("arn:${AWS::Partition}:logs:*:${AWS::AccountId}:log-group:*") + ] + }) + ] + }) + + const deployStageFirewallLoggingCWPolicyResource = deployStageFirewallLoggingCWPolicy.node.findChild('Resource') as iam.CfnPolicy; + + deployStageFirewallLoggingCWPolicyResource.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W12', + reason: 'Resource * is required for describe APIs' + }] + } + }; + + // create the 'deployStageFirewallLoggingCWPolicy' IAM policy if + // logging destination type is set to CloudWatch Logs + deployStageFirewallLoggingCWPolicyResource.cfnOptions.condition = isLoggingInCloudWatch + + // Code deploy build action project, role will be created by the construct. + + const deployProject = new PipelineProject(this, 'DeployProject', { + buildSpec: BuildSpec.fromObject({ + version: '0.2', + phases: { + install: { + 'runtime-versions': { + nodejs: '12' + }, + commands: [`export current=$(pwd)`, `export sourceCodeKey=$CODE_BUILD_SOURCE_CODE_S3_KEY`] + }, + pre_build: { + commands: [ + `cd $current`, + `pwd; ls -ltr`, + `echo 'Download Network Firewall Solution Package'`, + `aws s3 cp s3://${codeBuildStagesSourceCodeBucket.bucketName}/$sourceCodeKey/network-firewall-automation.zip $current`, + `unzip -o $current/network-firewall-automation.zip -d $current`, + `pwd; ls -ltr`, + ] + }, + build: { + commands: [ + `echo "Initiating Network Firewall Automation"`, + `node index.js` + ] + }, + post_build: { + commands: [] + } + }, + artifacts: { + files: "**/*" + } + }), + environment: { + buildImage: LinuxBuildImage.STANDARD_4_0 + }, + environmentVariables: codeBuildEnvVariables + }) + + // attach inline IAM policies with the default CodeBuild role. + deployProject.role?.attachInlinePolicy(deployStageFirewallPolicy) + deployProject.role?.attachInlinePolicy(deployStageFirewallLoggingPolicy) + deployProject.role?.attachInlinePolicy(deployStageFirewallLoggingS3Policy) + deployProject.role?.attachInlinePolicy(deployStageFirewallLoggingCWPolicy) + deployProject.role?.attachInlinePolicy(deployStageModifyTransitGatewayAttachmentPolicy) + + + const codePipeline = new codepipeline.Pipeline(this, `NetworkFirewallCodePipeline`, { + stages: [ + { + stageName: 'Source', + actions: [ + new codepipeline_action.CodeCommitSourceAction({ + actionName: 'Source', + repository: codeCommitRepo, + output: sourceOutputArtifact, + }) + ] + }, + { + stageName: 'Validation', + actions: [ + new codepipeline_action.CodeBuildAction({ + actionName: 'CodeBuild', + input: sourceOutputArtifact, + project: buildProject, + outputs: [buildOutputArtifact] + }) + ] + }, + { + stageName: 'Deployment', + actions: [ + new codepipeline_action.CodeBuildAction({ + actionName: 'CodeDeploy', + input: buildOutputArtifact, + project: deployProject, + }) + ] + }] + }) + + //Adding bucket encryption + const kmsKeyCfn_ref = codePipeline.artifactBucket.encryptionKey?.node.defaultChild as kms.CfnKey + kmsKeyCfn_ref.addPropertyOverride('EnableKeyRotation', true) + + const stack = cdk.Stack.of(this); + + const codePipelineArtifactBucketKmsKeyAlias = stack.node.findChild("NetworkFirewallCodePipeline").node.findChild("ArtifactsBucketEncryptionKeyAlias").node.defaultChild as kms.CfnAlias + codePipelineArtifactBucketKmsKeyAlias.addPropertyOverride("AliasName", { + "Fn::Join": [ + "", + [ + "alias/", + { + "Ref": "AWS::StackName" + }, + "-artifactBucket-EncryptionKeyAlias" + ] + ] + }) + + const codeBuildStagesSourceCodeBucket_cfn_ref = codeBuildStagesSourceCodeBucket.node.defaultChild as s3.CfnBucket + codeBuildStagesSourceCodeBucket_cfn_ref.bucketEncryption = { + serverSideEncryptionConfiguration: [ + { + serverSideEncryptionByDefault: { + kmsMasterKeyId: codePipeline.artifactBucket.encryptionKey?.keyArn, + sseAlgorithm: "aws:kms" + } + } + ] + } + + codeBuildStagesSourceCodeBucket_cfn_ref.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [{ + id: 'W35', + reason: 'Source Code bucket bucket does not require logging configuration' + }, { + id: 'W51', + reason: 'Source Code bucket is private and does not require a bucket policy' + }] + } + }; + + //S3 Bucket policy for the pipeline artifacts bucket + const bucketPolicy = new s3.BucketPolicy(this, 'CodePipelineArtifactS3BucketPolicy', { + bucket: codePipeline.artifactBucket, + removalPolicy: RemovalPolicy.RETAIN + }) + + bucketPolicy.document.addStatements( + new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: [ + 's3:DeleteBucket' + ], + principals: [new iam.ServicePrincipal('cloudformation.amazonaws.com')], + resources: [ + codePipeline.artifactBucket.bucketArn + ] + }), + new iam.PolicyStatement({ + effect: iam.Effect.DENY, + actions: [ + 's3:GetObject' + ], + principals: [ + new iam.AnyPrincipal() + ], + resources: [ + `${codePipeline.artifactBucket.bucketArn}/*`, + `${codePipeline.artifactBucket.bucketArn}` + ], + conditions: { + Bool: { + "aws:SecureTransport": false + } + } + })); + + const bucketPolicyForlogsBucket = new s3.BucketPolicy(this, 'CloudWatchLogsForNetworkFirewallBucketPolicy', { + bucket: logsBucket, + removalPolicy: RemovalPolicy.RETAIN + }) + + bucketPolicyForlogsBucket.document.addStatements( + new iam.PolicyStatement({ + effect: iam.Effect.DENY, + actions: [ + 's3:GetObject' + ], + principals: [ + new iam.AnyPrincipal() + ], + resources: [ + `${logsBucket.bucketArn}/*`, + `${logsBucket.bucketArn}` + ], + conditions: { + Bool: { + "aws:SecureTransport": false + } + } + })); + + const bucketPolicyForlogsBucket_cfn_ref = bucketPolicyForlogsBucket.node.defaultChild as s3.CfnBucketPolicy + bucketPolicyForlogsBucket_cfn_ref.cfnOptions.condition = isLoggingInS3 + + const bucketPolicyForSourceCodeBucket = new s3.BucketPolicy(this, 'CodeBuildStageSourceCodeBucketPolicy', { + bucket: codeBuildStagesSourceCodeBucket, + removalPolicy: RemovalPolicy.RETAIN + }); + + bucketPolicyForSourceCodeBucket.document.addStatements( + new iam.PolicyStatement({ + effect: iam.Effect.DENY, + actions: [ + 's3:GetObject' + ], + principals: [ + new iam.AnyPrincipal() + ], + resources: [ + `${codeBuildStagesSourceCodeBucket.bucketArn}`, + `${codeBuildStagesSourceCodeBucket.bucketArn}/*` + ], + conditions: { + Bool: { + "aws:SecureTransport": false + } + } + })); + + //disable W35 for the artifact bucket as it only store the artifact files. + const w35Rule = { + rules_to_suppress: [{ + id: 'W35', + reason: "This S3 bucket is used as the destination for 'NetworkFirewallCodePipelineArtifactsBucket'" + }] + } + const s3ArtifactBucket_cfn_ref = codePipeline.artifactBucket.node.defaultChild as s3.CfnBucket + s3ArtifactBucket_cfn_ref.cfnOptions.metadata = { + cfn_nag: w35Rule + } + + /** + * Outputs - describes the values that are returned whenever you view + * your stack's properties. + */ + new cdk.CfnOutput(this, 'Inspection VPC ID', { + value: vpc.ref, + description: 'Inspection VPC ID to create Network Firewall.', + }) + + new cdk.CfnOutput(this, 'Firewall Subnet 1 ID', { + value: NetworkFirewallSubnet1.ref, + description: 'Subnet 1 associated with Network Firewall.', + }) + + new cdk.CfnOutput(this, 'Firewall Subnet 2 ID', { + value: NetworkFirewallSubnet2.ref, + description: 'Subnet 2 associated with Network Firewall.', + }) + + new cdk.CfnOutput(this, 'Transit Gateway Subnet 1 ID', { + value: vpcTGWSubnet1.ref, + description: 'Subnet 1 associated with Transit Gateway.', + }) + + new cdk.CfnOutput(this, 'Transit Gateway Subnet 2 ID', { + value: vpcTGWSubnet2.ref, + description: 'Subnet 1 associated with Transit Gateway.', + }) + + new cdk.CfnOutput(this, 'Network Firewall Availability Zone 1', { + value: cdk.Fn.getAtt( + 'NetworkFirewallSubnet1', + 'AvailabilityZone').toString(), + description: 'Availability Zone configured for Network Firewall subnet 1', + }) + + new cdk.CfnOutput(this, 'Network Firewall Availability Zone 2', { + value: cdk.Fn.getAtt( + 'NetworkFirewallSubnet2', + 'AvailabilityZone').toString(), + description: 'Availability Zone configured for Network Firewall subnet 2', + }) + + new cdk.CfnOutput(this, 'Artifact Bucket for CodePipeline', { + value: codePipeline.artifactBucket.bucketName, + description: 'Artifact bucket name configured for the CodePipeline.', + }) + + new cdk.CfnOutput(this, 'Code Build source code bucket', { + value: codeBuildStagesSourceCodeBucket.bucketName, + description: 'Code Build source code bucket', + }) + + new cdk.CfnOutput(this, 'S3 Bucket for Firewall Logs', { + value: cdk.Fn.conditionIf('LoggingInS3', logsBucket.bucketName, 'NotConfigured').toString(), + description: 'S3 Bucket used as the log destination for Firewall' + + ' Logs.', + }) + + new cdk.CfnOutput(this, 'CloudWatch Log Group for Firewall Logs', { + value: cdk.Fn.conditionIf('LoggingInCloudWatch', cloudWatchLogGroup.ref, 'NotConfigured').toString(), + description: 'CloudWatch Log Group used as the log destination for Firewall' + + ' Logs.', + }) + + } +} diff --git a/source/networkFirewallAutomation/__tests__/ec2-manager.spec.ts b/source/networkFirewallAutomation/__tests__/ec2-manager.spec.ts new file mode 100644 index 0000000..aeeca0b --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/ec2-manager.spec.ts @@ -0,0 +1,92 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { Ec2Manager } from '../lib/ec2-manager'; + +const ec2EnvProps = [ + { + "routeTableId":"rtb-0e99886b16ecb5710", + "availabilityZone": 'us-east-1a' + }, + { + "routeTableId":"rtb-0e99886b16ecb5710", + "availabilityZone": 'us-east-1b' + }] + +jest.mock("aws-sdk", () => { + return { + __esModule: true, + EC2: jest.fn().mockReturnValue({ + + }) + } +}, { virtual: true }); + +jest.mock("../lib/service/ec2-service", () => { + return { + __esModule: true, + Ec2Service: jest.fn().mockReturnValue({ + describeRouteTables: jest.fn().mockImplementation(() => { + return [{"Associations":[{"Main":false,"RouteTableAssociationId":"rtbassoc-041509f1a595fa5dd","RouteTableId":"rtb-0e99886b16ecb5710","SubnetId":"subnet-028bf1f940038d771","AssociationState":{"State":"associated"}},{"Main":false,"RouteTableAssociationId":"rtbassoc-0c83e3ec6163f1999","RouteTableId":"rtb-0e99886b16ecb5710","SubnetId":"subnet-0884864b53eaf5171","AssociationState":{"State":"associated"}}],"PropagatingVgws":[],"RouteTableId":"rtb-0e99886b16ecb5710","Routes":[{"DestinationCidrBlock":"192.168.1.0/26","GatewayId":"local","Origin":"CreateRouteTable","State":"active"}],"Tags":[{"Key":"Name","Value":"FirewallSubnetRouteTable"}],"VpcId":"vpc-0ea9f7f530319814a","OwnerId":"1234"}] + }), + createRoute: jest.fn().mockImplementation(() => { + return { + 'Return': true + } + }) + }) + } +}, { virtual: true }); + +test('test the method routeTableOperations - 2 VPCE', async () => { + const syncStates = { + "us-east-1a": { + "Attachment": { + "SubnetId": "subnet-1", + "EndpointId": "vpce-1", + "Status": "READY" + }, + "Config": { + "arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1": {'SyncStatus': "IN_SYNC"}, + "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1": {'SyncStatus': "IN_SYNC"}, + "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1": {'SyncStatus': "IN_SYNC"}, + "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2": {'SyncStatus': "IN_SYNC"} + } + }, + "us-east-1b": { + "Attachment": { + "SubnetId": "subnet-2", + "EndpointId": "vpce-2", + "Status": "READY" + }, + "Config": { + "arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1": {'SyncStatus': "IN_SYNC"}, + "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1": {'SyncStatus': "IN_SYNC"}, + "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1": {'SyncStatus': "IN_SYNC"}, + "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2": {'SyncStatus': "IN_SYNC"} + } + } + + } + + const ec2Mgr = new Ec2Manager(ec2EnvProps, syncStates) + const response = await ec2Mgr.routeTableOperations() + console.log(response) + expect(response[0].VpcEndpointId).toStrictEqual("vpce-1") + expect(response[0].RouteTableId).toStrictEqual("rtb-0e99886b16ecb5710") + expect(response[0].DefaultRouteCreated).toStrictEqual(true) + expect(response[1].VpcEndpointId).toStrictEqual("vpce-2") + expect(response[0].RouteTableId).toStrictEqual("rtb-0e99886b16ecb5710") + expect(response[1].DefaultRouteCreated).toStrictEqual(true) + +}) diff --git a/source/networkFirewallAutomation/__tests__/firewall-config-validation.spec.ts b/source/networkFirewallAutomation/__tests__/firewall-config-validation.spec.ts new file mode 100644 index 0000000..13bad4b --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-config-validation.spec.ts @@ -0,0 +1,49 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { FirewallConfigValidation } from "../lib/common/firewall-config-validation" + +jest.mock("aws-sdk", () => { + return { + __esModule: true, + NetworkFirewall: jest.fn().mockReturnValue({ + createRuleGroup: jest.fn().mockImplementation(() => { + //console.log(`Inside rule group mock ${JSON.stringify(data)}` ) + }), + createFirewallPolicy: jest.fn().mockImplementation(() => { + //console.log(`Inside firewall policy mock ${JSON.stringify(data)}` ) + }), + }) + } +}) + +test('test firewall config validation.', async () => { + const firewallConfigValidation = new FirewallConfigValidation(); + try { + await firewallConfigValidation.execute("/__tests__/firewall-test-configuration/firewalls/") + } catch (error) { + expect(firewallConfigValidation.getInvalidFiles()).toStrictEqual([ + { + "path": "__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.invalid.json", + "referencedInFile": "__tests__/firewall-test-configuration/firewallPolicies/firewall-invalid-policy.json", + "error": "The file in the attribute path is not available in the configuration." + }, + { + "path": "__tests__/firewall-test-configuration/firewallPolicies/firewall-notavailable.json", + "referencedInFile": "__tests__/firewall-test-configuration/firewallPolicies/firewall-notavailable.json", + "error": "The file in the attribute path is not available in the configuration." + } + ]) + } + +}) \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-invalid-policy.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-invalid-policy.json new file mode 100644 index 0000000..929842a --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-invalid-policy.json @@ -0,0 +1,26 @@ +{ + "FirewallPolicyName": "Firewall-Policy-2", + "FirewallPolicy": { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.invalid.json" + }, + { + "Priority": 20, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-pass-action.example.json" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateful-domainblock.example.json" + } + ] + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-policy-2.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-policy-2.json new file mode 100644 index 0000000..1efe322 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-policy-2.json @@ -0,0 +1,26 @@ +{ + "FirewallPolicyName": "Firewall-Policy-2", + "FirewallPolicy": { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.example.json" + }, + { + "Priority": 20, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-pass-action.example.json" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateful-domainblock.example.json" + } + ] + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-policy.example.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-policy.example.json new file mode 100644 index 0000000..bb5ad6b --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewallPolicies/firewall-policy.example.json @@ -0,0 +1,29 @@ +{ + "FirewallPolicyName": "Firewall-Policy-1", + "FirewallPolicy": { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.example.json" + }, + { + "Priority": 20, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-pass-action.example.json" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateful-domainblock.example.json" + }, + { + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/suricata-rule-reference.json" + } + ] + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall-invalid.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall-invalid.json new file mode 100644 index 0000000..a9ea930 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall-invalid.json @@ -0,0 +1,8 @@ +{ + "FirewallName": "VpcFirewall-1", + "FirewallPolicyArn": "__tests__/firewall-test-configuration/firewallPolicies/firewall-invalid-policy.json", + "Description": "Network Firewall created by AWS Solutions", + "DeleteProtection": true, + "FirewallPolicyChangeProtection": true, + "SubnetChangeProtection": true + } \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall-nopolicy.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall-nopolicy.json new file mode 100644 index 0000000..ca7272e --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall-nopolicy.json @@ -0,0 +1,8 @@ +{ + "FirewallName": "VpcFirewall-1", + "FirewallPolicyArn": "__tests__/firewall-test-configuration/firewallPolicies/firewall-notavailable.json", + "Description": "Network Firewall created by AWS Solutions", + "DeleteProtection": true, + "FirewallPolicyChangeProtection": true, + "SubnetChangeProtection": true + } \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall.example.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall.example.json new file mode 100644 index 0000000..8bc8538 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/firewalls/firewall.example.json @@ -0,0 +1,8 @@ +{ + "FirewallName": "VpcFirewall-1", + "FirewallPolicyArn": "__tests__/firewall-test-configuration/firewallPolicies/firewall-policy.example.json", + "Description": "Network Firewall created by AWS Solutions", + "DeleteProtection": true, + "FirewallPolicyChangeProtection": true, + "SubnetChangeProtection": true +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/drop.rules b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/drop.rules new file mode 100644 index 0000000..e37904c --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/drop.rules @@ -0,0 +1,79 @@ +# +# $Id: emerging-drop.rules $ +# Emerging Threats Spamhaus DROP List rules. +# +# Rules to block Spamhaus DROP listed networks (www.spamhaus.org) +# +# More information available at www.emergingthreats.net +# +# Please submit any feedback or ideas to emerging@emergingthreats.net or the emerging-sigs mailing list +# +#************************************************************* +# +# Copyright (c) 2003-2020, Emerging Threats +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other materials provided with the distribution. +# * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# + +# VERSION 2793 + + +# Generated 2021-01-10 00:05:02 EDT + +alert ip [2.59.200.0/22,5.134.128.0/19,5.180.4.0/22,5.181.84.0/22,5.183.60.0/22,5.188.10.0/23,24.137.16.0/20,24.170.208.0/20,24.233.0.0/19,24.236.0.0/19,27.126.160.0/20,27.146.0.0/16,31.14.65.0/24,31.14.66.0/23,31.40.156.0/22,31.40.164.0/22,36.0.8.0/21,36.37.48.0/20,36.116.0.0/16,36.119.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 1"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400000; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [42.0.32.0/19,42.1.128.0/17,42.96.0.0/18,42.128.0.0/12,42.160.0.0/12,42.194.128.0/17,42.208.0.0/12,43.229.52.0/22,43.236.0.0/16,43.250.116.0/22,43.252.80.0/22,45.4.128.0/22,45.4.136.0/22,45.6.48.0/22,45.9.148.0/22,45.9.156.0/22,45.10.16.0/22,45.11.184.0/22,45.11.188.0/22,45.41.0.0/18] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 2"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400001; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [45.65.120.0/22,45.65.188.0/22,45.80.28.0/22,45.80.248.0/23,45.80.250.0/23,45.86.20.0/22,45.95.40.0/22,45.114.240.0/22,45.117.52.0/22,45.117.232.0/22,45.119.40.0/22,45.121.204.0/22,45.130.100.0/22,45.135.193.0/24,45.159.56.0/22,45.220.64.0/18,46.102.177.0/24,46.102.178.0/23,46.102.180.0/24,46.102.182.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 3"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400002; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [58.14.0.0/15,58.145.176.0/21,59.153.60.0/22,60.233.0.0/16,61.11.224.0/19,61.45.251.0/24,64.92.224.0/20,64.250.144.0/20,65.97.48.0/20,67.213.112.0/20,68.66.48.0/20,69.8.64.0/20,69.8.96.0/20,72.1.224.0/20,74.114.148.0/22,76.191.0.0/20,77.36.62.0/24,77.81.84.0/23,77.81.86.0/24,77.81.89.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 4"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400003; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [85.209.4.0/22,86.55.40.0/23,86.55.42.0/23,86.62.28.0/22,86.104.0.0/23,86.104.2.0/24,86.104.212.0/23,86.104.222.0/23,86.104.224.0/23,86.105.2.0/24,86.105.6.0/24,86.105.176.0/24,86.105.178.0/24,86.105.184.0/23,86.105.186.0/24,86.105.229.0/24,86.105.230.0/24,86.105.242.0/23,86.106.10.0/24,86.106.13.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 5"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400004; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [86.106.110.0/23,86.106.114.0/23,86.106.116.0/23,86.106.118.0/24,86.106.138.0/23,86.106.140.0/23,86.106.174.0/23,86.107.72.0/24,86.107.193.0/24,86.107.194.0/23,88.218.40.0/22,88.218.148.0/22,89.32.43.0/24,89.32.170.0/24,89.32.202.0/24,89.33.46.0/23,89.33.116.0/24,89.33.134.0/24,89.33.198.0/23,89.33.200.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 6"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400005; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [89.34.74.0/24,89.34.102.0/24,89.34.104.0/23,89.35.54.0/24,89.35.89.0/24,89.35.90.0/24,89.36.38.0/23,89.36.136.0/24,89.36.138.0/23,89.36.141.0/24,89.37.92.0/23,89.37.94.0/24,89.37.96.0/24,89.37.129.0/24,89.37.130.0/23,89.37.132.0/23,89.37.134.0/24,89.38.240.0/24,89.39.69.0/24,89.39.212.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 7"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400006; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [89.40.209.0/24,89.41.27.0/24,89.41.28.0/23,89.41.49.0/24,89.41.50.0/23,89.41.189.0/24,89.41.190.0/23,89.42.10.0/24,89.42.152.0/23,89.42.154.0/24,89.45.82.0/24,89.46.47.0/24,91.132.164.0/22,91.197.196.0/22,91.200.12.0/22,91.200.133.0/24,91.200.248.0/22,91.218.236.0/22,91.220.163.0/24,91.229.52.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 8"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400007; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [93.114.51.0/24,93.114.52.0/23,93.114.54.0/24,93.114.58.0/23,93.115.59.0/24,93.119.118.0/23,93.119.120.0/23,93.119.124.0/23,93.120.34.0/24,93.120.46.0/24,94.131.228.0/22,94.154.32.0/22,96.45.144.0/20,98.143.192.0/20,101.42.0.0/16,101.134.0.0/15,101.192.0.0/14,101.203.128.0/19,101.248.0.0/15,102.196.96.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 9"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400008; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [103.14.208.0/22,103.16.76.0/24,103.23.8.0/22,103.23.124.0/22,103.24.232.0/22,103.30.12.0/22,103.32.0.0/16,103.32.132.0/22,103.34.0.0/16,103.36.64.0/22,103.59.92.0/22,103.73.172.0/22,103.75.36.0/22,103.76.96.0/22,103.76.128.0/22,103.77.32.0/22,103.99.0.0/22,103.100.168.0/22,103.134.144.0/23,103.135.144.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 10"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400009; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [103.197.240.0/22,103.199.88.0/22,103.199.184.0/22,103.205.84.0/22,103.207.160.0/22,103.210.244.0/22,103.215.80.0/22,103.225.72.0/22,103.225.128.0/22,103.226.192.0/22,103.228.60.0/22,103.229.36.0/22,103.230.144.0/22,103.232.136.0/22,103.232.172.0/22,103.236.32.0/22,103.239.28.0/22,103.239.56.0/22,103.243.8.0/22,103.243.124.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 11"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400010; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [104.239.0.0/17,104.243.192.0/20,104.247.96.0/19,104.250.192.0/19,104.250.224.0/19,104.251.192.0/20,106.95.0.0/16,107.182.112.0/20,107.182.240.0/20,107.190.160.0/20,110.41.0.0/16,111.223.192.0/19,113.212.128.0/19,116.144.0.0/15,116.146.0.0/15,117.58.0.0/17,119.58.0.0/16,119.232.0.0/16,120.48.0.0/15,121.46.124.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 12"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400011; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [124.157.0.0/18,124.242.0.0/16,125.31.192.0/18,125.58.0.0/18,125.169.0.0/16,128.24.0.0/16,128.85.0.0/16,130.21.0.0/16,130.148.0.0/16,130.196.0.0/16,130.222.0.0/16,131.108.16.0/22,131.143.0.0/16,131.200.0.0/16,132.255.132.0/22,134.18.0.0/16,134.22.0.0/16,134.23.0.0/16,134.33.0.0/16,134.127.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 13"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400012; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [137.72.0.0/16,137.76.0.0/16,137.105.0.0/16,137.114.0.0/16,137.218.0.0/16,138.31.0.0/16,138.36.92.0/22,138.36.136.0/22,138.52.0.0/16,138.59.4.0/22,138.59.204.0/22,138.94.144.0/22,138.94.216.0/22,138.97.156.0/22,138.122.192.0/22,138.125.0.0/16,138.185.116.0/22,138.186.208.0/22,138.216.0.0/16,138.219.172.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 14"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400013; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [140.82.96.0/20,140.167.0.0/16,141.98.68.0/23,141.98.70.0/23,141.136.22.0/24,141.178.0.0/16,141.206.128.0/20,141.253.0.0/16,142.102.0.0/16,143.0.236.0/22,143.49.0.0/16,143.135.0.0/16,143.136.0.0/16,143.253.0.0/16,145.231.0.0/16,146.3.0.0/16,146.51.0.0/16,146.106.0.0/16,146.183.0.0/16,146.202.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 15"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400014; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [148.148.0.0/16,148.154.0.0/16,148.178.0.0/16,148.185.0.0/16,148.248.0.0/16,149.118.0.0/16,149.207.0.0/16,150.10.0.0/16,150.22.128.0/17,150.25.0.0/16,150.40.0.0/16,150.121.0.0/16,150.129.212.0/22,150.129.228.0/22,150.141.0.0/16,150.242.120.0/22,150.242.144.0/22,151.212.0.0/16,152.89.228.0/23,152.89.230.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 16"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400015; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [155.11.0.0/16,155.40.0.0/16,155.66.0.0/16,155.71.0.0/16,155.73.0.0/16,155.108.0.0/16,155.159.0.0/16,155.235.0.0/16,155.249.0.0/16,156.96.0.0/16,157.115.0.0/16,157.162.0.0/16,157.186.0.0/16,157.195.0.0/16,158.54.0.0/16,158.249.0.0/16,159.80.0.0/16,159.85.0.0/16,159.151.0.0/16,159.174.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 17"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400016; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [160.116.0.0/16,160.117.0.0/16,160.121.0.0/16,160.122.0.0/16,160.180.0.0/16,160.184.0.0/16,160.188.0.0/16,160.200.0.0/16,160.235.0.0/16,160.240.0.0/16,160.255.0.0/16,161.0.0.0/19,161.0.68.0/22,161.1.0.0/16,162.208.124.0/22,162.212.188.0/22,162.222.128.0/21,162.249.20.0/22,163.47.19.0/24,163.50.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 18"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400017; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [163.216.0.0/19,163.250.0.0/16,163.254.0.0/16,164.6.0.0/16,164.79.0.0/16,164.88.0.0/16,164.137.0.0/16,164.155.0.0/16,165.3.0.0/16,165.25.0.0/16,165.52.0.0/14,165.102.0.0/16,165.205.0.0/16,165.209.0.0/16,165.231.0.0/16,166.93.0.0/16,166.117.0.0/16,167.74.0.0/18,167.82.144.0/20,167.97.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 19"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400018; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [167.224.0.0/19,167.224.32.0/20,167.224.48.0/21,167.249.200.0/22,168.0.212.0/22,168.64.0.0/16,168.76.0.0/16,168.80.0.0/15,168.90.96.0/22,168.129.0.0/16,168.151.0.0/22,168.151.4.0/23,168.151.6.0/24,168.151.32.0/21,168.151.43.0/24,168.151.44.0/22,168.151.48.0/22,168.151.52.0/23,168.151.54.0/24,168.151.56.0/21] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 20"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400019; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [168.151.128.0/20,168.151.145.0/24,168.151.146.0/23,168.151.148.0/22,168.151.152.0/22,168.151.157.0/24,168.151.158.0/23,168.151.160.0/20,168.151.176.0/21,168.151.184.0/22,168.151.192.0/20,168.151.208.0/21,168.151.216.0/22,168.151.220.0/23,168.151.232.0/21,168.151.240.0/21,168.151.248.0/22,168.151.254.0/24,168.181.52.0/22,168.195.76.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 21"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400020; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [168.211.0.0/16,168.227.128.0/22,168.227.140.0/22,169.239.152.0/22,170.67.0.0/16,170.83.232.0/22,170.113.0.0/16,170.120.0.0/16,170.179.0.0/16,170.244.40.0/22,170.244.240.0/22,170.247.220.0/22,171.25.212.0/22,171.26.0.0/16,172.98.0.0/18,174.136.192.0/18,175.103.64.0/18,176.56.192.0/19,176.96.88.0/21,176.102.120.0/21] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 22"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400021; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [176.223.116.0/23,176.223.118.0/24,176.223.160.0/23,177.234.136.0/21,178.212.184.0/21,178.213.176.0/22,179.63.0.0/17,180.178.192.0/18,180.236.0.0/14,181.177.64.0/18,185.0.96.0/19,185.21.8.0/22,185.30.168.0/22,185.39.8.0/22,185.55.4.0/22,185.55.140.0/22,185.60.201.0/24,185.60.202.0/23,185.63.35.0/24,185.64.23.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 23"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400022; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [185.116.172.0/23,185.116.175.0/24,185.120.8.0/22,185.122.128.0/22,185.123.144.0/20,185.123.248.0/21,185.124.0.0/22,185.124.56.0/21,185.126.136.0/22,185.126.148.0/22,185.126.160.0/21,185.126.224.0/22,185.126.236.0/22,185.126.248.0/22,185.127.44.0/22,185.127.56.0/22,185.127.68.0/22,185.127.76.0/22,185.127.92.0/22,185.129.8.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 24"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400023; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [185.144.180.0/22,185.147.140.0/22,185.156.88.0/21,185.156.92.0/22,185.161.148.0/22,185.165.24.0/22,185.180.192.0/22,185.184.192.0/22,185.185.48.0/22,185.193.90.0/24,185.193.143.0/24,185.194.100.0/22,185.203.64.0/22,185.215.132.0/22,185.227.200.0/22,185.230.44.0/22,185.234.64.0/22,185.237.104.0/22,185.237.220.0/22,185.237.226.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 25"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400024; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [188.172.160.0/19,188.208.48.0/22,188.208.52.0/22,188.208.109.0/24,188.208.220.0/22,188.209.120.0/21,188.212.254.0/24,188.213.23.0/24,188.213.206.0/23,188.213.214.0/23,188.213.248.0/22,188.213.252.0/22,188.214.94.0/24,188.214.95.0/24,188.214.140.0/24,188.214.155.0/24,188.214.193.0/24,188.241.211.0/24,188.247.230.0/24,190.123.208.0/20] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 26"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400025; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [192.31.212.0/23,192.40.29.0/24,192.43.160.0/24,192.43.175.0/24,192.43.176.0/21,192.43.184.0/24,192.54.110.0/24,192.67.16.0/24,192.96.146.0/24,192.101.44.0/24,192.101.181.0/24,192.101.200.0/21,192.101.240.0/21,192.101.248.0/23,192.133.3.0/24,192.152.194.0/24,192.154.11.0/24,192.160.44.0/24,192.161.80.0/20,192.190.49.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 27"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400026; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [192.219.120.0/21,192.219.128.0/18,192.219.192.0/20,192.219.208.0/21,192.226.16.0/20,192.229.32.0/19,192.231.66.0/24,192.234.189.0/24,192.245.101.0/24,192.251.231.0/24,192.252.16.0/20,193.25.48.0/20,193.30.254.0/23,193.32.66.0/23,193.46.172.0/22,193.139.0.0/16,193.151.160.0/22,193.201.232.0/22,193.228.91.0/24,193.243.0.0/17] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 28"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400027; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [196.1.109.0/24,196.10.64.0/19,196.15.64.0/18,196.16.0.0/14,196.42.128.0/17,196.61.192.0/20,196.62.0.0/16,196.192.192.0/18,196.193.0.0/16,196.194.0.0/15,196.199.0.0/16,196.207.64.0/18,196.246.0.0/16,197.154.0.0/16,197.231.208.0/22,198.13.0.0/20,198.14.0.0/20,198.20.16.0/20,198.45.32.0/20,198.45.64.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 29"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400028; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [198.96.224.0/20,198.99.117.0/24,198.102.222.0/24,198.148.212.0/24,198.151.16.0/20,198.151.64.0/18,198.151.152.0/22,198.160.205.0/24,198.169.201.0/24,198.177.175.0/24,198.177.176.0/22,198.177.180.0/24,198.177.214.0/24,198.178.64.0/19,198.179.22.0/24,198.181.96.0/20,198.183.32.0/19,198.184.193.0/24,198.184.208.0/24,198.186.25.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 30"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400029; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [198.200.8.0/23,198.202.237.0/24,198.204.0.0/21,198.206.140.0/24,198.212.132.0/24,199.5.152.0/23,199.5.229.0/24,199.26.137.0/24,199.26.207.0/24,199.26.251.0/24,199.33.222.0/24,199.34.128.0/18,199.60.102.0/24,199.71.192.0/20,199.73.64.0/20,199.84.16.0/20,199.84.55.0/24,199.84.56.0/22,199.84.60.0/24,199.84.64.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 31"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400030; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [199.184.82.0/24,199.185.144.0/20,199.185.192.0/20,199.196.192.0/19,199.198.160.0/20,199.198.176.0/21,199.198.184.0/23,199.198.188.0/22,199.200.64.0/19,199.212.96.0/20,199.223.0.0/20,199.230.64.0/19,199.230.96.0/21,199.233.85.0/24,199.233.96.0/24,199.241.64.0/19,199.244.56.0/21,199.245.138.0/24,199.246.137.0/24,199.246.213.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 32"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400031; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [199.254.32.0/20,200.0.60.0/23,200.22.0.0/16,200.71.124.0/22,200.189.44.0/22,200.234.128.0/18,201.148.168.0/22,201.169.0.0/16,202.0.192.0/18,202.20.32.0/19,202.21.64.0/19,202.27.96.0/23,202.27.98.0/24,202.27.99.0/24,202.27.100.0/22,202.27.120.0/22,202.27.161.0/24,202.27.162.0/23,202.27.164.0/22,202.27.168.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 33"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400032; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [202.148.176.0/20,202.183.0.0/19,202.189.80.0/20,203.2.200.0/22,203.9.0.0/19,203.31.88.0/23,203.34.70.0/23,203.86.252.0/22,203.169.0.0/22,203.191.64.0/18,203.195.0.0/18,204.14.80.0/22,204.19.38.0/23,204.44.32.0/20,204.44.208.0/20,204.44.224.0/20,204.52.96.0/19,204.52.255.0/24,204.57.16.0/20,204.75.147.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 34"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400033; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [204.106.128.0/18,204.106.192.0/19,204.107.208.0/24,204.126.244.0/23,204.128.32.0/20,204.128.151.0/24,204.128.180.0/24,204.130.16.0/20,204.130.167.0/24,204.147.64.0/21,204.147.96.0/20,204.147.240.0/20,204.156.192.0/20,204.194.64.0/21,204.225.159.0/24,204.225.210.0/24,204.232.0.0/18,204.238.137.0/24,204.238.170.0/24,204.238.183.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 35"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400034; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [205.148.192.0/18,205.151.128.0/19,205.159.45.0/24,205.159.174.0/24,205.159.180.0/24,205.166.77.0/24,205.166.84.0/24,205.166.130.0/24,205.166.168.0/24,205.166.211.0/24,205.172.244.0/22,205.175.160.0/19,205.189.71.0/24,205.189.72.0/23,205.203.0.0/19,205.203.224.0/19,205.207.134.0/24,205.210.107.0/24,205.210.139.0/24,205.210.171.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 36"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400035; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [205.236.189.0/24,205.237.88.0/21,206.41.128.0/20,206.41.160.0/19,206.51.29.0/24,206.124.104.0/21,206.125.16.0/20,206.130.188.0/24,206.143.128.0/17,206.183.128.0/19,206.195.224.0/19,206.197.28.0/24,206.197.29.0/24,206.197.77.0/24,206.197.165.0/24,206.209.48.0/20,206.209.80.0/20,206.223.17.0/24,206.224.160.0/19,206.226.0.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 37"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400036; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [207.90.0.0/18,207.110.64.0/18,207.110.96.0/19,207.110.128.0/18,207.183.64.0/19,207.183.96.0/20,207.183.128.0/19,207.183.192.0/19,207.201.64.0/18,207.228.192.0/20,207.244.0.0/18,208.73.208.0/22,208.90.32.0/21,208.93.4.0/22,209.17.192.0/19,209.66.0.0/18,209.66.128.0/19,209.95.64.0/19,209.95.192.0/19,209.99.128.0/18] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 38"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400037; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [209.145.0.0/19,209.148.16.0/20,209.161.64.0/19,209.161.96.0/20,209.182.64.0/19,209.242.192.0/19,212.162.152.0/22,213.173.36.0/22,213.247.0.0/19,216.179.128.0/17,220.154.0.0/16,221.132.192.0/18,223.0.0.0/15,223.169.0.0/16,223.173.0.0/16,223.254.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 39"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400038; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateful-domainblock.example.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateful-domainblock.example.json new file mode 100644 index 0000000..ded9f03 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateful-domainblock.example.json @@ -0,0 +1,15 @@ +{ + "RuleGroupName": "StatefulRulesExample1", + "RuleGroup": { + "RulesSource": { + "RulesSourceList": { + "Targets": [ "test.example.com" ], + "TargetTypes": [ "HTTP_HOST", "TLS_SNI" ], + "GeneratedRulesType": "DENYLIST" + } + } + }, + "Type": "STATEFUL", + "Description": "Stateful Rule3", + "Capacity": 100 +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.example.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.example.json new file mode 100644 index 0000000..70a9a42 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.example.json @@ -0,0 +1,41 @@ +{ + "RuleGroupName": "StatelessExample2", + "RuleGroup": { + "RulesSource": { + "StatelessRulesAndCustomActions": { + "StatelessRules": [ + { + "RuleDefinition": { + "MatchAttributes": { + "Sources": [ + { + "AddressDefinition": "192.0.2.0/8" + } + ], + "Destinations": [ + { + "AddressDefinition": "124.1.1.5/32" + }, + { + "AddressDefinition": "198.51.100.0/16" + } + ], + "Protocols": [ + 6, + 17 + ] + }, + "Actions": [ + "aws:forward_to_sfe" + ] + }, + "Priority": 100 + } + ] + } + } + }, + "Type": "STATELESS", + "Description": "Stateless Rule with Forward to Stateful3", + "Capacity": 220 +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateless-pass-action.example.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateless-pass-action.example.json new file mode 100644 index 0000000..c97b849 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/stateless-pass-action.example.json @@ -0,0 +1,68 @@ +{ + "RuleGroupName": "StatelessExample1", + "RuleGroup": { + "RulesSource": { + "StatelessRulesAndCustomActions": { + "StatelessRules": [ + { + "RuleDefinition": { + "MatchAttributes": { + "Sources": [ + { + "AddressDefinition": "192.0.2.0/8" + } + ], + "Destinations": [ + { + "AddressDefinition": "198.51.100.0/16" + } + ], + "SourcePorts": [ + { + "FromPort": 53, + "ToPort": 53 + }, + { + "FromPort": 1001, + "ToPort": 1053 + } + ], + "DestinationPorts": [ + { + "FromPort": 53, + "ToPort": 53 + }, + { + "FromPort": 1001, + "ToPort": 1053 + } + ], + "Protocols": [ + 6 + ], + "TCPFlags": [ + { + "Flags": [ + "SYN" + ], + "Masks": [ + "SYN", + "ACK" + ] + } + ] + }, + "Actions": [ + "aws:pass" + ] + }, + "Priority": 19 + } + ] + } + } + }, + "Type": "STATELESS", + "Description": "Stateless Rule with pass action", + "Capacity": 199 +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/suricata-rule-reference.json b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/suricata-rule-reference.json new file mode 100644 index 0000000..1f99720 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/firewall-test-configuration/ruleGroups/suricata-rule-reference.json @@ -0,0 +1,8 @@ + +{ + "RuleGroupName": "suricata-icmp-rules2", + "Rules": "__tests__/firewall-test-configuration/ruleGroups/drop.rules", + "Type": "STATEFUL", + "Description": "Suricata rule group", + "Capacity": 100 + } \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/network-firewall-manager.spec.ts b/source/networkFirewallAutomation/__tests__/network-firewall-manager.spec.ts new file mode 100644 index 0000000..da0c3f5 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/network-firewall-manager.spec.ts @@ -0,0 +1,327 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { NetworkFirewallManager } from '../lib/network-firewall-manager'; +import { ConfigReader } from '../lib/common/configReader/config-reader'; + + +jest.mock("aws-sdk", () => { + return { + __esModule: true, + NetworkFirewall: jest.fn().mockReturnValue({ + + }) + } +}, { virtual: true }); + +jest.mock("../lib/service/network-firewall-service", () => { + return { + __esModule: true, + NetworkFirewallService: jest.fn().mockReturnValue({ + describeRuleGroup: jest.fn().mockImplementation((data) => { + const StatelessExample2Describe = { "UpdateToken": "c7007261-d236-4997-8eab-7e15445c84a2", "RuleGroupResponse": { "RuleGroupArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2", "RuleGroupName": "StatelessExample2", "RuleGroupId": "206bd83b-3b59-4000-9ff3-3fe369f34719", "Description": "Stateless Rule with Forward to Stateful3", "Type": "STATELESS", "Capacity": 220, "RuleGroupStatus": "ACTIVE", "Tags": [] } } + const StatelessExample1Describe = { "UpdateToken": "9b5bc310-99d4-45c9-a16e-bdb58f883a48", "RuleGroup": { "RulesSource": { "StatelessRulesAndCustomActions": { "StatelessRules": [{ "RuleDefinition": { "MatchAttributes": { "Sources": [{ "AddressDefinition": "192.0.2.0/8" }], "Destinations": [{ "AddressDefinition": "198.51.100.0/16" }], "SourcePorts": [{ "FromPort": 53, "ToPort": 53 }, { "FromPort": 1001, "ToPort": 1053 }], "DestinationPorts": [{ "FromPort": 53, "ToPort": 53 }, { "FromPort": 1001, "ToPort": 1053 }], "Protocols": [6], "TCPFlags": [{ "Flags": ["SYN"], "Masks": ["SYN", "ACK"] }] }, "Actions": ["aws:drop"] }, "Priority": 19 }], "CustomActions": [{ "ActionName": "CustomAction", "ActionDefinition": { "PublishMetricAction": { "Dimensions": [{ "Value": "test" }] } } }] } } }, "RuleGroupResponse": { "RuleGroupArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1", "RuleGroupName": "StatelessExample1", "RuleGroupId": "7246cfe2-00c7-4ef9-8d47-2b80bf8840e5", "Description": "Stateless Rule with Custom Action2", "Type": "STATELESS", "Capacity": 199, "RuleGroupStatus": "ACTIVE", "Tags": [] } } + const StatefulRulesExample1Describe = { "UpdateToken": "dd7696c5-e2cd-4882-a560-21e28570fc0f", "RuleGroup": { "RulesSource": { "RulesSourceList": { "Targets": ["test.example.com"], "TargetTypes": ["HTTP_HOST", "TLS_SNI"], "GeneratedRulesType": "DENYLIST" } } }, "RuleGroupResponse": { "RuleGroupArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1", "RuleGroupName": "StatefulRulesExample1", "RuleGroupId": "2560e622-5d9e-4c5c-9680-958bcb5c231b", "Description": "Stateful Rule2", "Type": "STATEFUL", "Capacity": 100, "RuleGroupStatus": "ACTIVE", "Tags": [] } } + const suricataRuleGroup = { + UpdateToken: '72e4e89b-acec-4184-b033-2dab8dd2a35f', + RuleGroupResponse: { + RuleGroupArn: 'arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/suricata-icmp-rules2', + RuleGroupName: 'suricata-icmp-rules2', + RuleGroupId: 'f593c04a-079c-423f-8558-b02a8c0edb0e', + Type: 'STATEFUL', + Capacity: 300, + RuleGroupStatus: 'ACTIVE' + } + } + + if (data === 'StatelessExample2') { + return StatelessExample2Describe + } else if (data === 'StatelessExample1') { + return StatelessExample1Describe + } else if (data === 'StatefulRulesExample1') { + return StatefulRulesExample1Describe; + } else if(data === 'suricata-icmp-rules2') { + return suricataRuleGroup; + } + return '' + }), + updateRuleGroup: jest.fn().mockImplementation((data) => { + const StatelessExample2Update = { "UpdateToken": "7fa52fd2-6b3a-41c5-8356-359d17a01ac0", "RuleGroup": { "RulesSource": { "StatelessRulesAndCustomActions": { "StatelessRules": [{ "RuleDefinition": { "MatchAttributes": { "Sources": [{ "AddressDefinition": "192.0.2.0/8" }], "Destinations": [{ "AddressDefinition": "124.1.1.5/32" }, { "AddressDefinition": "198.51.100.0/16" }], "Protocols": [6, 17] }, "Actions": ["aws:forward_to_sfe"] }, "Priority": 100 }] } } }, "RuleGroupResponse": { "RuleGroupArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2", "RuleGroupName": "StatelessExample2", "RuleGroupId": "206bd83b-3b59-4000-9ff3-3fe369f34719", "Description": "Stateless Rule with Forward to Stateful2", "Type": "STATELESS", "Capacity": 220, "RuleGroupStatus": "ACTIVE", "Tags": [] } } + const StatelessExample1Update = { "UpdateToken": "327d0dca-e671-46bc-9ed7-83cf51773868", "RuleGroupResponse": { "RuleGroupArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1", "RuleGroupName": "StatelessExample1", "RuleGroupId": "7246cfe2-00c7-4ef9-8d47-2b80bf8840e5", "Description": "Stateless Rule with Custom Action3", "Type": "STATELESS", "Capacity": 199, "RuleGroupStatus": "ACTIVE", "Tags": [] } } + const StatefulRulesExample1Update = { "UpdateToken": "cc4687e1-f370-4e10-abfc-12984e1d62e7", "RuleGroupResponse": { "RuleGroupArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1", "RuleGroupName": "StatefulRulesExample1", "RuleGroupId": "2560e622-5d9e-4c5c-9680-958bcb5c231b", "Description": "Stateful Rule3", "Type": "STATEFUL", "Capacity": 100, "RuleGroupStatus": "ACTIVE", "Tags": [] } } + if (data["RuleGroupArn"] === 'arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2') { + return StatelessExample2Update + } else if (data["RuleGroupArn"] === 'arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1') { + return StatelessExample1Update + } else if (data["RuleGroupArn"] === 'arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1') { + return StatefulRulesExample1Update; + } + return '' + }), + createRuleGroup: jest.fn().mockImplementation(() => { + //console.log(`Inside createRuleGroup mock ${JSON.stringify(data)}`); + }), + listRuleGroupsForPolicy: jest.fn().mockImplementation(() => { + return '' + }), + describeFirewall: jest.fn().mockImplementation(() => { + //console.log(`Inside describeFirewall mock ${JSON.stringify(data)}`); + return { Firewall: { "FirewallName": "VpcFirewall-1", "FirewallPolicyArn": "arn:aws:network-firewall:us-east-1:1234::firewall/*", "Description": "NetworkFirewallcreatedbyAWSSolutions", "VpcId": "vpc-1", "SubnetMappings": [{ "SubnetId": "subnet-1" }, { "SubnetId": "subnet-2" },], "DeleteProtection": true, "SubnetChangeProtection": true, "FirewallPolicyChangeProtection": true, "FirewallId": "string", "Tags": [{ "Key": "string", "Value": "string" },] }, FirewallStatus: { "Status": "READY", "ConfigurationSyncStateSummary": "IN_SYNC", "SyncStates": { "us-east-1a": { "Attachment": { "SubnetId": "subnet-1", "EndpointId": "vpce-1", "Status": "READY" }, "Config": { "arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2": {'SyncStatus': "IN_SYNC"} } }, "us-east-1b": { "Attachment": { "SubnetId": "subnet-2", "EndpointId": "vpce-2", "Status": "READY" }, "Config": { "arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2": {'SyncStatus': "IN_SYNC"} } } } } } + }), + describeFirewallPolicy: jest.fn().mockImplementation((data) => { + if (data && data === "Firewall-Policy-2") { + return Promise.resolve({ + UpdateToken: 'aaa', + FirewallPolicyResponse: { + FirewallPolicyName: 'Firewall-Policy-2', + FirewallPolicyArn: 'arn:aws', + FirewallPolicyId: 100 + } + }) + } + return Promise.resolve() + }), + createFirewallPolicy: jest.fn().mockImplementation(() => { + //console.log(`Inside describeFirewallPolicy mock ${JSON.stringify(data)}`); + return { "FirewallPolicyResponse": { "FirewallPolicyName": "Firewall-Policy-1", "Description": "FirewallPolicy1", "FirewallPolicyArn": "arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1", "FirewallPolicyStatus": "ACTIVE", "Tags": [{ "Key": "string", "Value": "string" }] }, "FirewallPolicy": { "StatelessDefaultActions": ["aws:drop"], "StatelessFragmentDefaultActions": ["aws:drop"], "StatelessRuleGroupReferences": [{ "Priority": 30, "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2" }, { "Priority": 20, "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1" }], "StatefulRuleGroupReferences": [{ "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1" }] } } + }), + createFirewall: jest.fn().mockImplementation(() => { + //console.log(`Inside describeFirewallPolicy mock ${JSON.stringify(data)}`); + return { "FirewallResponse": { "Firewall": { "FirewallName": "VpcFirewall-1", "FirewallPolicyArn": "arn:aws:network-firewall:us-east-1:1234::firewall/*", "Description": "NetworkFirewallcreatedbyAWSSolutions", "VpcId": "vpc-1", "SubnetMappings": [{ "SubnetId": "subnet-1" }, { "SubnetId": "subnet-2" }], "DeleteProtection": true, "SubnetChangeProtection": true, "FirewallPolicyChangeProtection": true, "FirewallId": "string", "Tags": [{ "Key": "string", "Value": "string" }] }, "FirewallStatus": { "Status": "READY", "ConfigurationSyncStateSummary": "IN_SYNC", "SyncStates": { "us-east-1a": { "Attachment": { "SubnetId": "subnet-1", "EndpointId": "vpce-1", "Status": "READY" }, "Config": { "arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2": {'SyncStatus': "IN_SYNC"} } }, "us-east-1b": { "Attachment": { "SubnetId": "subnet-2", "EndpointId": "vpce-2", "Status": "READY" }, "Config": { "arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1": {'SyncStatus': "IN_SYNC"}, "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2": {'SyncStatus': "IN_SYNC"} } } } } } } + }), + updateLoggingConfiguration: jest.fn().mockImplementation(() => { + return {} + }), + updateFirewallPolicy: jest.fn().mockImplementation(() => { + return { + + } + }), + associateFirewallPolicy: jest.fn().mockImplementation((data) => { + expect(data["FirewallPolicyArn"]).toBe("arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1"); + }), + updateFirewallDeleteProtection: jest.fn().mockImplementation((data) => { + expect(data["DeleteProtection"]).toBeTruthy(); + }), + updateFirewallPolicyChangeProtection: jest.fn().mockImplementation((data) => { + expect(data["FirewallPolicyChangeProtection"]).toBeTruthy() + }), + updateSubnetChangeProtection: jest.fn().mockImplementation((data) => { + expect(data["SubnetChangeProtection"]).toBeTruthy(); + }), + updateFirewallDescription: jest.fn().mockImplementation((data) => { + expect(data["Description"]).toBe("Network Firewall created by AWS Solutions") + }) + }) + } +}, { virtual: true }); + +test('test the method ruleGroupExist.', async () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: '', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: '' }, firewallObject, new ConfigReader()); + + //load the firewall policy + const policyObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewallPolicies/firewall-policy.example.json') + + const response = await managerInstance.ruleGroupOperations(policyObject); + + expect(response).toStrictEqual({ "FirewallPolicyName": "Firewall-Policy-1", "FirewallPolicy": { "StatelessDefaultActions": ["aws:drop"], "StatelessFragmentDefaultActions": ["aws:drop"], "StatelessRuleGroupReferences": [{ "Priority": 30, "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2" }, { "Priority": 20, "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1" }], "StatefulRuleGroupReferences": [{ "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1" }, { "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/suricata-icmp-rules2" }] } }) +}) + +test('test the method ruleGroupExist error scenario.', async () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: '', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: '' }, firewallObject, new ConfigReader()); + + const policyObject = { + FirewallPolicyName: 'Firewall-Policy-1', + FirewallPolicy: { + StatelessDefaultActions: ['aws:drop'], + StatelessFragmentDefaultActions: ['aws:drop'], + StatelessRuleGroupReferences: [{ + "Priority": 30, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-fwd-to-stateful.example.json" + }, + { + "Priority": 20, + "ResourceArn": "__tests__/firewall-test-configuration/ruleGroups/stateless-pass-action.example.json" + }], + StatefulRuleGroupReferences: [{ + "ResourceArn": "error" + }] + } + } + + await expect(managerInstance.ruleGroupOperations(policyObject)).rejects.toThrowError("Error: ENOENT: no such file or directory, open 'error'") + +}) + +test('test the method firewallExist.', async () => { + + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: 'vpc-1', subnetIds: 'subnet-1, subnet-2', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + + const response = await managerInstance.firewallOperations(); + expect(response).toStrictEqual({ + 'us-east-1a': { + Attachment: { SubnetId: 'subnet-1', EndpointId: 'vpce-1', Status: 'READY' }, + Config: { + 'arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1': {'SyncStatus': "IN_SYNC"}, + 'arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1': {'SyncStatus': "IN_SYNC"}, + 'arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1': {'SyncStatus': "IN_SYNC"}, + 'arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2': {'SyncStatus': "IN_SYNC"} + } + }, + 'us-east-1b': { + Attachment: { SubnetId: 'subnet-2', EndpointId: 'vpce-2', Status: 'READY' }, + Config: { + 'arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1': {'SyncStatus': "IN_SYNC"}, + 'arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1': {'SyncStatus': "IN_SYNC"}, + 'arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1': {'SyncStatus': "IN_SYNC"}, + 'arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2': {'SyncStatus': "IN_SYNC"} + } + } + }) +}) + + +test('firewall policy already exists', async () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: 'vpc-1', subnetIds: 'subnet-1, subnet-2', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + + const response = await managerInstance.firewallPolicyOperations("__tests__/firewall-test-configuration/firewallPolicies/firewall-policy-2.json") + expect(response).toBe("arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1") +}) + + +test('test the logging configuration object creation from environment variables', async () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + let managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: '', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + + let loggingConfiguration = await managerInstance.createLoggingConfigurations() + + expect(loggingConfiguration.length).toBe(1) + expect(loggingConfiguration[0].LogType).toBe("ALERT") + expect(loggingConfiguration[0].LogDestinationType).toBe("S3") + expect(JSON.stringify(loggingConfiguration[0].LogDestination)).toStrictEqual("{\"bucketName\":\"test-bucket\",\"prefix\":\"alerts\"}") + + + managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: '', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'FLOW', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + + loggingConfiguration = await managerInstance.createLoggingConfigurations() + + expect(loggingConfiguration.length).toBe(1) + expect(loggingConfiguration[0].LogType).toBe("FLOW") + expect(loggingConfiguration[0].LogDestinationType).toBe("S3") + expect(JSON.stringify(loggingConfiguration[0].LogDestination)).toStrictEqual("{\"bucketName\":\"test-bucket\",\"prefix\":\"flow\"}") + + managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: '', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'EnableBoth', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + + loggingConfiguration = await managerInstance.createLoggingConfigurations() + + expect(loggingConfiguration.length).toBe(2) + expect(loggingConfiguration[0].LogType).toBe("ALERT") + expect(loggingConfiguration[0].LogDestinationType).toBe("S3") + expect(JSON.stringify(loggingConfiguration[0].LogDestination)).toStrictEqual("{\"bucketName\":\"test-bucket\",\"prefix\":\"alerts\"}") + expect(loggingConfiguration[1].LogType).toBe("FLOW") + expect(loggingConfiguration[1].LogDestinationType).toBe("S3") + expect(JSON.stringify(loggingConfiguration[1].LogDestination)).toStrictEqual("{\"bucketName\":\"test-bucket\",\"prefix\":\"flow\"}") + + managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: '', logDestinationType: 'CloudWatchLogs', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'EnableBoth', logDestination: 'log-group-name' }, firewallObject, new ConfigReader()); + + loggingConfiguration = await managerInstance.createLoggingConfigurations() + + expect(loggingConfiguration.length).toBe(2) + expect(loggingConfiguration[0].LogType).toBe("ALERT") + expect(loggingConfiguration[0].LogDestinationType).toBe("CloudWatchLogs") + expect(JSON.stringify(loggingConfiguration[0].LogDestination)).toStrictEqual("{\"logGroup\":\"log-group-name\"}") + + expect(loggingConfiguration[1].LogType).toBe("FLOW") + expect(loggingConfiguration[1].LogDestinationType).toBe("CloudWatchLogs") + expect(JSON.stringify(loggingConfiguration[1].LogDestination)).toStrictEqual("{\"logGroup\":\"log-group-name\"}") +}); + +test('subnet mappings function should return an array', () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: 'vpc-1', subnetIds: 'subnet-1, subnet-2', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + expect(managerInstance.getSubnetMapping()).toStrictEqual([ { SubnetId: 'subnet-1' }, { SubnetId: ' subnet-2' } ]) +}) +test('subnet mappings function should return an array --error scenario', () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: 'vpc-1', subnetIds: '', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + try { + managerInstance.getSubnetMapping() + } catch(error) { + expect(error["message"]).toBe("Subnet IDs must be in the environment variables") + } +}) + +test('vpc id should be return from environment variable', () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: 'vpc-1', subnetIds: 'subnet-1, subnet-2', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + expect(managerInstance.getVpcId()).toBe("vpc-1") +}) + +test('vpc id should be return from environment variable --error scenario', () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: 'subnet-1, subnet-2', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + try { + managerInstance.getVpcId() + } catch (error) { + expect(error["message"]).toBe("VPC ID must be in the environment variables") + } +}) + +test('Update firewall properties', async () => { + const fileHandler = new ConfigReader(); + let firewallObject = fileHandler.convertFileToObject('__tests__/firewall-test-configuration/firewalls/firewall.example.json') + const managerInstance = new NetworkFirewallManager( + { vpcId: '', subnetIds: 'subnet-1, subnet-2', logDestinationType: 'S3', logRetentionPeriod: "90", stackId : 'f449b250-b969-11e0-a185-5081d0136786', logType: 'ALERT', logDestination: 'test-bucket' }, firewallObject, new ConfigReader()); + + await managerInstance.updateFirewall({ + Firewall: { + FirewallId: '12345', + FirewallPolicyArn: 'arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-2', + SubnetMappings: [], + VpcId: '', + DeleteProtection: false, + Description: '', + FirewallName: 'VpcFirewall-1', + FirewallArn: '', + FirewallPolicyChangeProtection: false, + SubnetChangeProtection: false + } + }, 'arn:aws:network-firewall:us-east-1:1234:firewall-policy/Firewall-Policy-1') + +}) + diff --git a/source/networkFirewallAutomation/__tests__/network-firewall-service.spec.ts b/source/networkFirewallAutomation/__tests__/network-firewall-service.spec.ts new file mode 100644 index 0000000..8bd18e7 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/network-firewall-service.spec.ts @@ -0,0 +1,740 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { NetworkFirewallService } from '../lib/service/network-firewall-service'; + +jest.mock("aws-sdk", () => { + return { + __esModule: true, + NetworkFirewall: jest.fn().mockReturnValue({ + deleteRuleGroup: jest.fn().mockImplementation((data) => { + expect(data['RuleGroupArn']).toBeDefined(); + return { + promise: jest.fn().mockImplementation(() => { + return Promise.resolve( + { + ResourceArn: '', + ResourceName: 'rg1', + Description: '', + UpdateToken: '', + RulesSource: {} + }) + }) + } + }), + describeRuleGroup: jest.fn().mockImplementation((ruleGroup) => { + if (ruleGroup["RuleGroupName"] === "ThrottlingException") { + throw { + "message": "ThrottlingException" + } + } + if (ruleGroup["RuleGroupName"] === "ResourceNotFoundException") { + throw {"code": "ResourceNotFoundException"}; + } + if (ruleGroup["RuleGroupName"] === "Error") { + return Promise.reject({ + message: "Error" + }) + } + if (ruleGroup["RuleGroupArn"] === "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2") { + return { + promise: jest.fn().mockReturnValue({ + UpdateToken: "aaaa", + RuleGroupResponse: { + RuleGroupArn: "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2", + RuleGroupName: "StatelessExample2", + RuleGroupId: 111 + } + }) + } + } + return { + promise: jest.fn().mockReturnValue({ + RuleGroup: { + RuleVariables: { + IPSets: [{ + "foo": { + Definition: [''], + Reference: 'AWS_ARN' + } + }], + PortSets: [{ + "foo": { + Definition: [''] + } + }] + }, + RulesSource: { + RulesString: '', + RulesSourceList: [{ + Targets: [''], + TargetType: [''], + GeneratedRulesType: '' + }], + StatefulRules: [{ + Action: '', + Header: { + Protocol: '', + Source: '', + SourcePort: '', + Direction: '', + Destination: '', + DestinationPort: '' + }, + RuleOptions: [{ + Keyword: '', + Settings: [''] + }] + }], + StatelessRulesAndCustomActions: { + StatelessRules: [{ + RuleDefinition: { + MatchAttributes: { + Sources: [''], + Destinations: [''], + SourcePorts: [{ + FromPort: 0, + ToPort: 999 + }], + DestinationPorts: [{ + FromPort: 0, + ToPort: 999 + }], + Protocols: [0, 1, 2, 3], + TCPFlags: [{ + Flags: [''], + Masks: [''] + }] + }, + Actions: [''] + }, + Priority: 9999 + }], + CustomAction: { + PublishMetrics: { + Dimensions: [{ + Value: '' + }] + } + } + } + } + }, + RuleGroupResponse: { + RuleGroupArn: '', + RuleGroupName: '', + RuleGroupId: '', + Description: '', + Type: '', + Capacity: 9999, + RuleGroupStatus: 'ACTIVE|DELETING|string', + Tags: [{ + Key: '', + Value: '' + }] + }, + UpdateToken: 'aaa' + }) + } + }), + describeFirewallPolicy: jest.fn().mockImplementation(() => { + return { + promise: jest.fn().mockReturnValue({ + UpdateToken: 'aaaa', + FirewallPolicyResponse: { + FirewallPolicyName: 'test-firewall-policy', + FirewallPolicyArn: '', + FirewallPolicyId: '', + Description: '', + FirewallPolicyStatus: 'ACTIVE', + Tags: [{ + Key: '', + Value: '' + }] + }, + FirewallPolicy: { + StatelessRuleGroupReferences: [{ + ResourceArn: 'arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2', + Priority: 999 + }], + StatelessDefaultActions: [''], + StatelessFragmentDefaultActions: [''], + StatelessCustomActions: [{ + ActionName: '', + CustomAction: { + PublishMetrics: { + Dimensions: [{ + Value: '' + }] + } + } + }], + StatefulRuleGroupReferences: [{ + ResourceArn: '' + }] + + } + }) + } + }), + updateRuleGroup: jest.fn().mockImplementation((data) =>{ + if (data['UpdateToken'] === 'invalid token') { + return { + promise: jest.fn().mockReturnValue({ + message: 'Update token is invalid.' + }) + } + } + if (data["UpdateToken"] === "error") { + return { + promise: jest.fn().mockReturnValue( + Promise.reject()) + } + } + + return { + promise: jest.fn().mockReturnValue({ + UpdateToken: '', + RuleGroupResponse: { + RuleGroupArn: '', + RuleGroupName: '', + RuleGroupId: '', + Description: '', + Type: '"STATELESS"|"STATEFUL"|string', + Capacity: 999, + RuleGroupStatus: '"ACTIVE"|"DELETING"|string', + Tags: [{ + Key: '', + Value: '' + }] + } + })} + }), + updateFirewallPolicy: jest.fn().mockImplementation((data) => { + if (data && data["UpdateToken"] === "test invalid token scenario") { + throw { + message: "Update token is invalid." + } + } + if (data && data["UpdateToken"] === "error") { + throw { + "message": "error" + } + } + + return { + promise: jest.fn().mockReturnValue({ + UpdateToken: 'aaa', + FirewallPolicyResponse: { + FirewallPolicyName: '', + FirewallPolicyArn: '', + FirewallPolicyId: '', + Description: '', + FirewallPolicyStatus: '"ACTIVE"|"DELETING"|string', + Tags: [{ + Key: '', + Value: '' + }] + } + }) + } + }), + listFirewalls: jest.fn().mockReturnValue({ + promise: jest.fn().mockReturnValue({}) + }), + createFirewall: jest.fn().mockImplementation((data) => { + + if (data["Description"] === "Error") { + throw Error("ResourceNotFoundException") + } + return { + promise: jest.fn().mockReturnValue({}) + + } + }), + createFirewallPolicy: jest.fn().mockReturnValue({ + promise: jest.fn().mockReturnValue({ + + }) + }), + createRuleGroup: jest.fn().mockReturnValue({ + promise: jest.fn().mockReturnValue({ + + }) + }), + describeFirewall: jest.fn().mockImplementation((data) => { + if (data["FirewallName"] === "error") { + throw Error("ResourceNotFoundException") + } + expect(data["FirewallName"]).toBeDefined(); + return { + promise: jest.fn().mockReturnValue({ + + }) + } + }), + describeLoggingConfiguration: jest.fn().mockReturnValue({ + promise: jest.fn().mockReturnValue({ + LoggingConfiguration: { + LogDestinationConfigs: [{ + LogType: 'ALERT', + LogDestinationType: 'CloudWatchLogs', + LogDestination: { + 'logGroup': "network-firewall-automation-solution", + 'prefix': 'alerts' + } + }] + } + }) + }), + updateLoggingConfiguration: jest.fn().mockImplementation((config)=> { + if(config["LoggingConfiguration"]["LogDestinationConfigs"][0] === undefined) { + + return { + promise: jest.fn().mockReturnValue({ + LoggingConfiguration: { + LogDestinationConfigs: [] + } + }) + } + } + if (config["LoggingConfiguration"]["LogDestinationConfigs"][0]["LogDestinationType"] === "CloudWatchLogs") { + return { + promise: jest.fn().mockReturnValue({ + LoggingConfiguration: { + LogDestinationConfigs: [] + } + }) + } + } + + + return { + promise: jest.fn().mockReturnValue({ + LoggingConfiguration: { + LogDestinationConfigs: [config["LoggingConfiguration"]["LogDestinationConfigs"][0]] + } + }) + } + }), + associateFirewallPolicy: jest.fn().mockImplementation((data) => { + + if (data && data["FirewallName"] === "error") { + throw { + "message": "error" + } + } + return {promise: jest.fn().mockReturnValue({ + + })} + }), + updateSubnetChangeProtection: jest.fn().mockImplementation((data) => { + if (data && data["FirewallName"] === "error") { + throw { + "message": "error" + } + } + return {promise: jest.fn().mockReturnValue({ + + })} + }), + updateFirewallDescription: jest.fn().mockImplementation((data) => { + if (data && data["FirewallName"] === "error") { + throw { + "message": "error" + } + } + return {promise: jest.fn().mockReturnValue({ + + })} + }), + updateFirewallPolicyChangeProtection: jest.fn().mockImplementation((data) => { + if (data && data["FirewallName"] === "error") { + throw { + "message": "error" + } + } + return {promise: jest.fn().mockReturnValue({ + + })} + }), + updateFirewallDeleteProtection: jest.fn().mockImplementation((data) => { + if (data && data["FirewallName"] === "error") { + throw { + "message": "error" + } + } + return {promise: jest.fn().mockReturnValue({ + + })} + }) + }) + } +}, { virtual: true }); + + + +test('test describe firewall policy', async () => { + + const service = new NetworkFirewallService(); + await expect(service.describeFirewallPolicy( +'test-network-firewall' + )).resolves.toBeDefined() +}) + + +test('test describe rule group', async () => { + const service = new NetworkFirewallService() + await expect(service.describeRuleGroup('test-stateless-rg1', 'STATEFUL')).resolves.toBeDefined(); +}) + +test('test describe rule group throttling error response', async () => { + const service = new NetworkFirewallService() + await expect(service.describeRuleGroup('ThrottlingException', 'STATEFUL')).rejects.toStrictEqual({"message":"ThrottlingException"}); +}) +test('test describe rule group resource not found exception response', async () => { + const service = new NetworkFirewallService() + await expect(service.describeRuleGroup('ResourceNotFoundException', 'STATEFUL')).resolves.toBeUndefined(); +}) + +test('create firewall ', async () => { + const service = new NetworkFirewallService() + await expect(service.createFirewall({ + "FirewallName": "VpcFirewall-1", + "FirewallPolicyArn": "__tests__/firewall-test-configuration/firewallPolicies/firewall-policy.example.json", + "Description": "Network Firewall created by AWS Solutions", + "DeleteProtection": true, + "FirewallPolicyChangeProtection": true, + "SubnetChangeProtection": true, + "SubnetMappings": [], + "VpcId": '', + "Tags": [{ + "Key": "SampleKey", + "Value": "SampleValue" + }] + })).resolves.toBeDefined() +}) +test('create firewall handle error response from the sdk. ', async () => { + const service = new NetworkFirewallService() + await expect(service.createFirewall({ + "FirewallName": "VpcFirewall-1", + "FirewallPolicyArn": "__tests__/firewall-test-configuration/firewallPolicies/firewall-policy.example.json", + "Description": "Error", + "DeleteProtection": true, + "FirewallPolicyChangeProtection": true, + "SubnetChangeProtection": true, + "SubnetMappings": [], + "VpcId": '', + "Tags": [{ + "Key": "SampleKey", + "Value": "SampleValue" + }] + })).rejects.toThrowError("ResourceNotFoundException") +}) + +test('create Firewall policy', async () => { + const service = new NetworkFirewallService() + await expect(service.createFirewallPolicy({ + "FirewallPolicyName": "Firewall-Policy-1", + "FirewallPolicy": { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2" + }, + { + "Priority": 20, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1" + } + ] + } + })).resolves.toBeDefined(); + +}) + +test('create rule group', async () => { + + const service = new NetworkFirewallService() + await expect(service.createRuleGroup({ + "RuleGroupName": "StatefulRulesExample1", + "RuleGroup": { + "RulesSource": { + "RulesSourceList": { + "Targets": ["test.example.com"], + "TargetTypes": ["HTTP_HOST", "TLS_SNI"], + "GeneratedRulesType": "DENYLIST" + } + } + }, + "Type": "STATEFUL", + "Description": "Stateful Rule3", + "Capacity": 100 + })).resolves.toBeDefined(); + +}) + +test(' describe firewall', async () => { + const service = new NetworkFirewallService() + await expect(service.describeFirewall('firewall-name')).resolves.toBeDefined(); +}) + +test(' describe firewall handle sdk error', async () => { + const service = new NetworkFirewallService() + await expect(service.describeFirewall('error')) + .rejects.toThrowError("ResourceNotFoundException") +}) + +test('Update firewall policy ', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallPolicy({ + UpdateToken: '', + FirewallPolicyArn: '', + FirewallPolicyName: 'test', + FirewallPolicy: { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2" + }, + { + "Priority": 20, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1" + } + ] + } + })).resolves.toBeDefined() +}) + +test('Update firewall policy handle invalid token scenario.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallPolicy({ + UpdateToken: 'test invalid token scenario', + FirewallPolicyArn: '', + FirewallPolicyName: 'test', + FirewallPolicy: { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2" + }, + { + "Priority": 20, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1" + } + ] + } + })).resolves.toBeDefined() +}) +test('Update firewall policy handle error.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallPolicy({ + UpdateToken: 'error', + FirewallPolicyArn: '', + FirewallPolicyName: 'test', + FirewallPolicy: { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample2" + }, + { + "Priority": 20, + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateless-rulegroup/StatelessExample1" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "arn:aws:network-firewall:us-east-1:1234:stateful-rulegroup/StatefulRulesExample1" + } + ] + } + })).rejects.toBeDefined() +}) + + +test('Update rule groups', async () => { + + const service = new NetworkFirewallService() + await expect(service.updateRuleGroup({ + UpdateToken: '', + RuleGroupName: 'test' + })).resolves.toBeDefined() + +}) +test('Update rule groups handle invalid token error', async () => { + + const service = new NetworkFirewallService() + await expect(service.updateRuleGroup({ + UpdateToken: 'invalid token', + RuleGroupName: 'test' + })).resolves.toBeDefined() + +}) +test('Update rule groups handle error', async () => { + + const service = new NetworkFirewallService() + await expect(service.updateRuleGroup({ + UpdateToken: 'error', + RuleGroupName: 'test' + })).rejects.toThrowError() + +}) + +test('Update logging configuration', async () => { + const service = new NetworkFirewallService() + const response = await service.updateLoggingConfiguration('firewallName', { + LogDestinationConfigs: [{ + LogType: 'ALERT', + LogDestination: { + 'bucketName': "network-firewall-automation-solution", + 'prefix': "alerts" + }, + LogDestinationType: 'S3' + }] + }) + expect(response).toStrictEqual({"LoggingConfiguration":{"LogDestinationConfigs":[{"LogType":"ALERT","LogDestination":{"bucketName":"network-firewall-automation-solution","prefix":"alerts"},"LogDestinationType":"S3"}]}}) +}) + +test('List rule groups for firewall Policy', async () => { + const service = new NetworkFirewallService() + await expect(service.listRuleGroupsForPolicy('FirewallName')).resolves.toBeDefined() +}) + +test('delete rule Group', async () => { + const service = new NetworkFirewallService() + await expect(service.deleteRuleGroup('')).resolves.toBeUndefined() +}) + +test('associate firewall policy', async () => { + + const service = new NetworkFirewallService(); + await expect(service.associateFirewallPolicy({ + FirewallPolicyArn: '', + FirewallName: '' + })).resolves.toBeDefined(); + +}) + +test('associate firewall policy error response', async () => { + + const service = new NetworkFirewallService(); + await expect(service.associateFirewallPolicy({ + FirewallPolicyArn: '', + FirewallName: 'error' + })).rejects.toBeDefined(); + +}) + +test('update firewall description.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallDescription({ + Description: '', + FirewallName: '' + })).resolves.toBeDefined(); + +}) + +test('associate firewall description error response', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallDescription({ + Description: '', + FirewallName: 'error' + })).rejects.toBeDefined(); +}) + +test('update firewall deletion protection.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallDeleteProtection({ + DeleteProtection: false, + FirewallName: '' + })).resolves.toBeDefined(); +}) + +test('associate firewall deletion protection error response', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallDeleteProtection({ + DeleteProtection: false, + FirewallName: 'error' + })).rejects.toBeDefined(); +}) + +test('update firewall policy change protection.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallPolicyChangeProtection({ + FirewallPolicyChangeProtection: false, + FirewallName: '' + })).resolves.toBeDefined(); +}) + +test('update firewall policy change protection error response.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateFirewallPolicyChangeProtection({ + FirewallPolicyChangeProtection: false, + FirewallName: 'error' + })).rejects.toBeDefined(); +}) + +test('update subnet change protection.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateSubnetChangeProtection({ + SubnetChangeProtection: false, + FirewallName: '' + })).resolves.toBeDefined(); +}) + +test('update subnet change protection error response.', async () => { + const service = new NetworkFirewallService(); + await expect(service.updateSubnetChangeProtection({ + SubnetChangeProtection: false, + FirewallName: 'error' + })).rejects.toBeDefined(); +}) \ No newline at end of file diff --git a/source/networkFirewallAutomation/__tests__/send-metrics.spec.ts b/source/networkFirewallAutomation/__tests__/send-metrics.spec.ts new file mode 100644 index 0000000..f4a38f4 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/send-metrics.spec.ts @@ -0,0 +1,74 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +import { MetricsManager } from "../lib/common/send-metrics" + +jest.mock("aws-sdk", () => { + return { + __esModule: true, + SSM: jest.fn().mockReturnValue({ + getParameter: jest.fn().mockImplementation((data) => { + expect(data).toStrictEqual({ Name: 'network-firewall-solution-uuid-asds' }) + if ('network-firewall-solution-uuid-asds' === data["Name"]) { + return { + promise: jest.fn().mockReturnValue({ + Parameter: { + Value: '5d358dfa-bc71-4a48-a00c-0931e8ec1456' + } + }) + } + } else { + return { + promise: jest.fn().mockReturnValue({ + + }) + } + } + }) + }) + } +}, { virtual: true }); + +jest.mock("uuid", () => { + return { + __esModule: true, + v4: jest.fn().mockImplementation(() => { + return '5d358dfa-bc71-4a48-a00c-0931e8ec1456' + }) + } +}, { virtual: true }); + +jest.mock("axios", () => { + return { + __esModule: true, + post: jest.fn().mockImplementation(() => { + return { + promise: jest.fn().mockReturnValue({ + + }) + } + }) + } +}, { virtual: true }); + +test('test sending the metrics when the uuid is already in the parameter store.', async () => { + process.env.STACK_ID = 'asds' + process.env.SEND_ANONYMOUS_METRICS = 'Yes' + await MetricsManager.sendMetrics({ + numberOfFirewalls: 1, + numberOfPolicies: 1, + numberOfStatefulRuleGroups: 1, + numberOfStatelessRuleGroups: 1, + numberOfSuricataRules: 0 + }) +}) + diff --git a/source/networkFirewallAutomation/__tests__/stringManipulation.spec.ts b/source/networkFirewallAutomation/__tests__/stringManipulation.spec.ts new file mode 100644 index 0000000..743b503 --- /dev/null +++ b/source/networkFirewallAutomation/__tests__/stringManipulation.spec.ts @@ -0,0 +1,32 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { StringUtils, Name } from '../lib/common/stringUtils'; + +const stackId = 'f449b250-b969-11e0-a185-5081d0136786' + +test('test resource name less than 128 chars', async () => { + const resourceName = 'Firewall-1' + const stringMod = new StringUtils(stackId) + const customName = stringMod.getUniqueResourceName(resourceName) + console.log(customName) + expect(customName.length < Name.maxCharacters) +}) + +test('test resource name more than 128 chars', async () => { + const resourceName = 'Firewall-1-f449b250-b969-11e0-a185-5081d0136786-f449b250-b969-11e0-a185-5081d0136786-f449b250-b969-11e0-a185-9-11e0-a185-5081d0136786-f449b250-b969-11e0-a185' + const stringMod = new StringUtils(stackId) + const customName = stringMod.getUniqueResourceName(resourceName) + console.log(customName) + expect(customName.length == Name.maxCharacters) +}) diff --git a/source/networkFirewallAutomation/build.ts b/source/networkFirewallAutomation/build.ts new file mode 100644 index 0000000..59a0817 --- /dev/null +++ b/source/networkFirewallAutomation/build.ts @@ -0,0 +1,27 @@ + +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +import { FirewallConfigValidation } from "./lib/common/firewall-config-validation" +import { Logger, LOG_LEVEL } from "./lib/common/logger"; + +async function main() { + try { + const firewallConfigValidation = new FirewallConfigValidation() + await firewallConfigValidation.execute(); + } catch(error) { + Logger.log(LOG_LEVEL.ERROR, `Error in firewall config validation`, error) + process.exit(1) + } +} + +main(); \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/examples/firewallPolicies/firewall-policy.example.json b/source/networkFirewallAutomation/config/examples/firewallPolicies/firewall-policy.example.json new file mode 100644 index 0000000..abf1fd3 --- /dev/null +++ b/source/networkFirewallAutomation/config/examples/firewallPolicies/firewall-policy.example.json @@ -0,0 +1,30 @@ +{ + "FirewallPolicyName": "Firewall-Policy-1", + "Description": "Firewall Policy 1", + "FirewallPolicy": { + "StatelessDefaultActions": [ + "aws:drop" + ], + "StatelessFragmentDefaultActions": [ + "aws:drop" + ], + "StatelessRuleGroupReferences": [ + { + "Priority": 30, + "ResourceArn": "./ruleGroups/stateless-fwd-to-stateful.example.json" + }, + { + "Priority": 20, + "ResourceArn": "./ruleGroups/stateless-pass-action.example.json" + } + ], + "StatefulRuleGroupReferences": [ + { + "ResourceArn": "./ruleGroups/stateful-domainblock.example.json" + }, + { + "ResourceArn": "./ruleGroups/suricata-rule-reference.json" + } + ] + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/examples/firewalls/firewall.example.json b/source/networkFirewallAutomation/config/examples/firewalls/firewall.example.json new file mode 100644 index 0000000..a831ca5 --- /dev/null +++ b/source/networkFirewallAutomation/config/examples/firewalls/firewall.example.json @@ -0,0 +1,12 @@ +{ + "FirewallName": "VpcFirewall-1", + "FirewallPolicyArn": "./firewallPolicies/firewall-policy.example.json", + "Description": "Network Firewall created by AWS Solutions", + "DeleteProtection": true, + "FirewallPolicyChangeProtection": true, + "SubnetChangeProtection": true, + "Tags": [{ + "Key": "SampleKey", + "Value": "SampleValue" + }] +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/examples/ruleGroups/drop.rules b/source/networkFirewallAutomation/config/examples/ruleGroups/drop.rules new file mode 100644 index 0000000..e37904c --- /dev/null +++ b/source/networkFirewallAutomation/config/examples/ruleGroups/drop.rules @@ -0,0 +1,79 @@ +# +# $Id: emerging-drop.rules $ +# Emerging Threats Spamhaus DROP List rules. +# +# Rules to block Spamhaus DROP listed networks (www.spamhaus.org) +# +# More information available at www.emergingthreats.net +# +# Please submit any feedback or ideas to emerging@emergingthreats.net or the emerging-sigs mailing list +# +#************************************************************* +# +# Copyright (c) 2003-2020, Emerging Threats +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +# following disclaimer in the documentation and/or other materials provided with the distribution. +# * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# + +# VERSION 2793 + + +# Generated 2021-01-10 00:05:02 EDT + +alert ip [2.59.200.0/22,5.134.128.0/19,5.180.4.0/22,5.181.84.0/22,5.183.60.0/22,5.188.10.0/23,24.137.16.0/20,24.170.208.0/20,24.233.0.0/19,24.236.0.0/19,27.126.160.0/20,27.146.0.0/16,31.14.65.0/24,31.14.66.0/23,31.40.156.0/22,31.40.164.0/22,36.0.8.0/21,36.37.48.0/20,36.116.0.0/16,36.119.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 1"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400000; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [42.0.32.0/19,42.1.128.0/17,42.96.0.0/18,42.128.0.0/12,42.160.0.0/12,42.194.128.0/17,42.208.0.0/12,43.229.52.0/22,43.236.0.0/16,43.250.116.0/22,43.252.80.0/22,45.4.128.0/22,45.4.136.0/22,45.6.48.0/22,45.9.148.0/22,45.9.156.0/22,45.10.16.0/22,45.11.184.0/22,45.11.188.0/22,45.41.0.0/18] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 2"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400001; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [45.65.120.0/22,45.65.188.0/22,45.80.28.0/22,45.80.248.0/23,45.80.250.0/23,45.86.20.0/22,45.95.40.0/22,45.114.240.0/22,45.117.52.0/22,45.117.232.0/22,45.119.40.0/22,45.121.204.0/22,45.130.100.0/22,45.135.193.0/24,45.159.56.0/22,45.220.64.0/18,46.102.177.0/24,46.102.178.0/23,46.102.180.0/24,46.102.182.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 3"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400002; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [58.14.0.0/15,58.145.176.0/21,59.153.60.0/22,60.233.0.0/16,61.11.224.0/19,61.45.251.0/24,64.92.224.0/20,64.250.144.0/20,65.97.48.0/20,67.213.112.0/20,68.66.48.0/20,69.8.64.0/20,69.8.96.0/20,72.1.224.0/20,74.114.148.0/22,76.191.0.0/20,77.36.62.0/24,77.81.84.0/23,77.81.86.0/24,77.81.89.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 4"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400003; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [85.209.4.0/22,86.55.40.0/23,86.55.42.0/23,86.62.28.0/22,86.104.0.0/23,86.104.2.0/24,86.104.212.0/23,86.104.222.0/23,86.104.224.0/23,86.105.2.0/24,86.105.6.0/24,86.105.176.0/24,86.105.178.0/24,86.105.184.0/23,86.105.186.0/24,86.105.229.0/24,86.105.230.0/24,86.105.242.0/23,86.106.10.0/24,86.106.13.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 5"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400004; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [86.106.110.0/23,86.106.114.0/23,86.106.116.0/23,86.106.118.0/24,86.106.138.0/23,86.106.140.0/23,86.106.174.0/23,86.107.72.0/24,86.107.193.0/24,86.107.194.0/23,88.218.40.0/22,88.218.148.0/22,89.32.43.0/24,89.32.170.0/24,89.32.202.0/24,89.33.46.0/23,89.33.116.0/24,89.33.134.0/24,89.33.198.0/23,89.33.200.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 6"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400005; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [89.34.74.0/24,89.34.102.0/24,89.34.104.0/23,89.35.54.0/24,89.35.89.0/24,89.35.90.0/24,89.36.38.0/23,89.36.136.0/24,89.36.138.0/23,89.36.141.0/24,89.37.92.0/23,89.37.94.0/24,89.37.96.0/24,89.37.129.0/24,89.37.130.0/23,89.37.132.0/23,89.37.134.0/24,89.38.240.0/24,89.39.69.0/24,89.39.212.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 7"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400006; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [89.40.209.0/24,89.41.27.0/24,89.41.28.0/23,89.41.49.0/24,89.41.50.0/23,89.41.189.0/24,89.41.190.0/23,89.42.10.0/24,89.42.152.0/23,89.42.154.0/24,89.45.82.0/24,89.46.47.0/24,91.132.164.0/22,91.197.196.0/22,91.200.12.0/22,91.200.133.0/24,91.200.248.0/22,91.218.236.0/22,91.220.163.0/24,91.229.52.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 8"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400007; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [93.114.51.0/24,93.114.52.0/23,93.114.54.0/24,93.114.58.0/23,93.115.59.0/24,93.119.118.0/23,93.119.120.0/23,93.119.124.0/23,93.120.34.0/24,93.120.46.0/24,94.131.228.0/22,94.154.32.0/22,96.45.144.0/20,98.143.192.0/20,101.42.0.0/16,101.134.0.0/15,101.192.0.0/14,101.203.128.0/19,101.248.0.0/15,102.196.96.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 9"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400008; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [103.14.208.0/22,103.16.76.0/24,103.23.8.0/22,103.23.124.0/22,103.24.232.0/22,103.30.12.0/22,103.32.0.0/16,103.32.132.0/22,103.34.0.0/16,103.36.64.0/22,103.59.92.0/22,103.73.172.0/22,103.75.36.0/22,103.76.96.0/22,103.76.128.0/22,103.77.32.0/22,103.99.0.0/22,103.100.168.0/22,103.134.144.0/23,103.135.144.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 10"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400009; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [103.197.240.0/22,103.199.88.0/22,103.199.184.0/22,103.205.84.0/22,103.207.160.0/22,103.210.244.0/22,103.215.80.0/22,103.225.72.0/22,103.225.128.0/22,103.226.192.0/22,103.228.60.0/22,103.229.36.0/22,103.230.144.0/22,103.232.136.0/22,103.232.172.0/22,103.236.32.0/22,103.239.28.0/22,103.239.56.0/22,103.243.8.0/22,103.243.124.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 11"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400010; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [104.239.0.0/17,104.243.192.0/20,104.247.96.0/19,104.250.192.0/19,104.250.224.0/19,104.251.192.0/20,106.95.0.0/16,107.182.112.0/20,107.182.240.0/20,107.190.160.0/20,110.41.0.0/16,111.223.192.0/19,113.212.128.0/19,116.144.0.0/15,116.146.0.0/15,117.58.0.0/17,119.58.0.0/16,119.232.0.0/16,120.48.0.0/15,121.46.124.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 12"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400011; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [124.157.0.0/18,124.242.0.0/16,125.31.192.0/18,125.58.0.0/18,125.169.0.0/16,128.24.0.0/16,128.85.0.0/16,130.21.0.0/16,130.148.0.0/16,130.196.0.0/16,130.222.0.0/16,131.108.16.0/22,131.143.0.0/16,131.200.0.0/16,132.255.132.0/22,134.18.0.0/16,134.22.0.0/16,134.23.0.0/16,134.33.0.0/16,134.127.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 13"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400012; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [137.72.0.0/16,137.76.0.0/16,137.105.0.0/16,137.114.0.0/16,137.218.0.0/16,138.31.0.0/16,138.36.92.0/22,138.36.136.0/22,138.52.0.0/16,138.59.4.0/22,138.59.204.0/22,138.94.144.0/22,138.94.216.0/22,138.97.156.0/22,138.122.192.0/22,138.125.0.0/16,138.185.116.0/22,138.186.208.0/22,138.216.0.0/16,138.219.172.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 14"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400013; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [140.82.96.0/20,140.167.0.0/16,141.98.68.0/23,141.98.70.0/23,141.136.22.0/24,141.178.0.0/16,141.206.128.0/20,141.253.0.0/16,142.102.0.0/16,143.0.236.0/22,143.49.0.0/16,143.135.0.0/16,143.136.0.0/16,143.253.0.0/16,145.231.0.0/16,146.3.0.0/16,146.51.0.0/16,146.106.0.0/16,146.183.0.0/16,146.202.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 15"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400014; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [148.148.0.0/16,148.154.0.0/16,148.178.0.0/16,148.185.0.0/16,148.248.0.0/16,149.118.0.0/16,149.207.0.0/16,150.10.0.0/16,150.22.128.0/17,150.25.0.0/16,150.40.0.0/16,150.121.0.0/16,150.129.212.0/22,150.129.228.0/22,150.141.0.0/16,150.242.120.0/22,150.242.144.0/22,151.212.0.0/16,152.89.228.0/23,152.89.230.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 16"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400015; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [155.11.0.0/16,155.40.0.0/16,155.66.0.0/16,155.71.0.0/16,155.73.0.0/16,155.108.0.0/16,155.159.0.0/16,155.235.0.0/16,155.249.0.0/16,156.96.0.0/16,157.115.0.0/16,157.162.0.0/16,157.186.0.0/16,157.195.0.0/16,158.54.0.0/16,158.249.0.0/16,159.80.0.0/16,159.85.0.0/16,159.151.0.0/16,159.174.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 17"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400016; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [160.116.0.0/16,160.117.0.0/16,160.121.0.0/16,160.122.0.0/16,160.180.0.0/16,160.184.0.0/16,160.188.0.0/16,160.200.0.0/16,160.235.0.0/16,160.240.0.0/16,160.255.0.0/16,161.0.0.0/19,161.0.68.0/22,161.1.0.0/16,162.208.124.0/22,162.212.188.0/22,162.222.128.0/21,162.249.20.0/22,163.47.19.0/24,163.50.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 18"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400017; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [163.216.0.0/19,163.250.0.0/16,163.254.0.0/16,164.6.0.0/16,164.79.0.0/16,164.88.0.0/16,164.137.0.0/16,164.155.0.0/16,165.3.0.0/16,165.25.0.0/16,165.52.0.0/14,165.102.0.0/16,165.205.0.0/16,165.209.0.0/16,165.231.0.0/16,166.93.0.0/16,166.117.0.0/16,167.74.0.0/18,167.82.144.0/20,167.97.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 19"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400018; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [167.224.0.0/19,167.224.32.0/20,167.224.48.0/21,167.249.200.0/22,168.0.212.0/22,168.64.0.0/16,168.76.0.0/16,168.80.0.0/15,168.90.96.0/22,168.129.0.0/16,168.151.0.0/22,168.151.4.0/23,168.151.6.0/24,168.151.32.0/21,168.151.43.0/24,168.151.44.0/22,168.151.48.0/22,168.151.52.0/23,168.151.54.0/24,168.151.56.0/21] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 20"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400019; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [168.151.128.0/20,168.151.145.0/24,168.151.146.0/23,168.151.148.0/22,168.151.152.0/22,168.151.157.0/24,168.151.158.0/23,168.151.160.0/20,168.151.176.0/21,168.151.184.0/22,168.151.192.0/20,168.151.208.0/21,168.151.216.0/22,168.151.220.0/23,168.151.232.0/21,168.151.240.0/21,168.151.248.0/22,168.151.254.0/24,168.181.52.0/22,168.195.76.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 21"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400020; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [168.211.0.0/16,168.227.128.0/22,168.227.140.0/22,169.239.152.0/22,170.67.0.0/16,170.83.232.0/22,170.113.0.0/16,170.120.0.0/16,170.179.0.0/16,170.244.40.0/22,170.244.240.0/22,170.247.220.0/22,171.25.212.0/22,171.26.0.0/16,172.98.0.0/18,174.136.192.0/18,175.103.64.0/18,176.56.192.0/19,176.96.88.0/21,176.102.120.0/21] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 22"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400021; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [176.223.116.0/23,176.223.118.0/24,176.223.160.0/23,177.234.136.0/21,178.212.184.0/21,178.213.176.0/22,179.63.0.0/17,180.178.192.0/18,180.236.0.0/14,181.177.64.0/18,185.0.96.0/19,185.21.8.0/22,185.30.168.0/22,185.39.8.0/22,185.55.4.0/22,185.55.140.0/22,185.60.201.0/24,185.60.202.0/23,185.63.35.0/24,185.64.23.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 23"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400022; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [185.116.172.0/23,185.116.175.0/24,185.120.8.0/22,185.122.128.0/22,185.123.144.0/20,185.123.248.0/21,185.124.0.0/22,185.124.56.0/21,185.126.136.0/22,185.126.148.0/22,185.126.160.0/21,185.126.224.0/22,185.126.236.0/22,185.126.248.0/22,185.127.44.0/22,185.127.56.0/22,185.127.68.0/22,185.127.76.0/22,185.127.92.0/22,185.129.8.0/22] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 24"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400023; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [185.144.180.0/22,185.147.140.0/22,185.156.88.0/21,185.156.92.0/22,185.161.148.0/22,185.165.24.0/22,185.180.192.0/22,185.184.192.0/22,185.185.48.0/22,185.193.90.0/24,185.193.143.0/24,185.194.100.0/22,185.203.64.0/22,185.215.132.0/22,185.227.200.0/22,185.230.44.0/22,185.234.64.0/22,185.237.104.0/22,185.237.220.0/22,185.237.226.0/23] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 25"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400024; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [188.172.160.0/19,188.208.48.0/22,188.208.52.0/22,188.208.109.0/24,188.208.220.0/22,188.209.120.0/21,188.212.254.0/24,188.213.23.0/24,188.213.206.0/23,188.213.214.0/23,188.213.248.0/22,188.213.252.0/22,188.214.94.0/24,188.214.95.0/24,188.214.140.0/24,188.214.155.0/24,188.214.193.0/24,188.241.211.0/24,188.247.230.0/24,190.123.208.0/20] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 26"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400025; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [192.31.212.0/23,192.40.29.0/24,192.43.160.0/24,192.43.175.0/24,192.43.176.0/21,192.43.184.0/24,192.54.110.0/24,192.67.16.0/24,192.96.146.0/24,192.101.44.0/24,192.101.181.0/24,192.101.200.0/21,192.101.240.0/21,192.101.248.0/23,192.133.3.0/24,192.152.194.0/24,192.154.11.0/24,192.160.44.0/24,192.161.80.0/20,192.190.49.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 27"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400026; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [192.219.120.0/21,192.219.128.0/18,192.219.192.0/20,192.219.208.0/21,192.226.16.0/20,192.229.32.0/19,192.231.66.0/24,192.234.189.0/24,192.245.101.0/24,192.251.231.0/24,192.252.16.0/20,193.25.48.0/20,193.30.254.0/23,193.32.66.0/23,193.46.172.0/22,193.139.0.0/16,193.151.160.0/22,193.201.232.0/22,193.228.91.0/24,193.243.0.0/17] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 28"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400027; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [196.1.109.0/24,196.10.64.0/19,196.15.64.0/18,196.16.0.0/14,196.42.128.0/17,196.61.192.0/20,196.62.0.0/16,196.192.192.0/18,196.193.0.0/16,196.194.0.0/15,196.199.0.0/16,196.207.64.0/18,196.246.0.0/16,197.154.0.0/16,197.231.208.0/22,198.13.0.0/20,198.14.0.0/20,198.20.16.0/20,198.45.32.0/20,198.45.64.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 29"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400028; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [198.96.224.0/20,198.99.117.0/24,198.102.222.0/24,198.148.212.0/24,198.151.16.0/20,198.151.64.0/18,198.151.152.0/22,198.160.205.0/24,198.169.201.0/24,198.177.175.0/24,198.177.176.0/22,198.177.180.0/24,198.177.214.0/24,198.178.64.0/19,198.179.22.0/24,198.181.96.0/20,198.183.32.0/19,198.184.193.0/24,198.184.208.0/24,198.186.25.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 30"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400029; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [198.200.8.0/23,198.202.237.0/24,198.204.0.0/21,198.206.140.0/24,198.212.132.0/24,199.5.152.0/23,199.5.229.0/24,199.26.137.0/24,199.26.207.0/24,199.26.251.0/24,199.33.222.0/24,199.34.128.0/18,199.60.102.0/24,199.71.192.0/20,199.73.64.0/20,199.84.16.0/20,199.84.55.0/24,199.84.56.0/22,199.84.60.0/24,199.84.64.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 31"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400030; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [199.184.82.0/24,199.185.144.0/20,199.185.192.0/20,199.196.192.0/19,199.198.160.0/20,199.198.176.0/21,199.198.184.0/23,199.198.188.0/22,199.200.64.0/19,199.212.96.0/20,199.223.0.0/20,199.230.64.0/19,199.230.96.0/21,199.233.85.0/24,199.233.96.0/24,199.241.64.0/19,199.244.56.0/21,199.245.138.0/24,199.246.137.0/24,199.246.213.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 32"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400031; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [199.254.32.0/20,200.0.60.0/23,200.22.0.0/16,200.71.124.0/22,200.189.44.0/22,200.234.128.0/18,201.148.168.0/22,201.169.0.0/16,202.0.192.0/18,202.20.32.0/19,202.21.64.0/19,202.27.96.0/23,202.27.98.0/24,202.27.99.0/24,202.27.100.0/22,202.27.120.0/22,202.27.161.0/24,202.27.162.0/23,202.27.164.0/22,202.27.168.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 33"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400032; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [202.148.176.0/20,202.183.0.0/19,202.189.80.0/20,203.2.200.0/22,203.9.0.0/19,203.31.88.0/23,203.34.70.0/23,203.86.252.0/22,203.169.0.0/22,203.191.64.0/18,203.195.0.0/18,204.14.80.0/22,204.19.38.0/23,204.44.32.0/20,204.44.208.0/20,204.44.224.0/20,204.52.96.0/19,204.52.255.0/24,204.57.16.0/20,204.75.147.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 34"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400033; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [204.106.128.0/18,204.106.192.0/19,204.107.208.0/24,204.126.244.0/23,204.128.32.0/20,204.128.151.0/24,204.128.180.0/24,204.130.16.0/20,204.130.167.0/24,204.147.64.0/21,204.147.96.0/20,204.147.240.0/20,204.156.192.0/20,204.194.64.0/21,204.225.159.0/24,204.225.210.0/24,204.232.0.0/18,204.238.137.0/24,204.238.170.0/24,204.238.183.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 35"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400034; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [205.148.192.0/18,205.151.128.0/19,205.159.45.0/24,205.159.174.0/24,205.159.180.0/24,205.166.77.0/24,205.166.84.0/24,205.166.130.0/24,205.166.168.0/24,205.166.211.0/24,205.172.244.0/22,205.175.160.0/19,205.189.71.0/24,205.189.72.0/23,205.203.0.0/19,205.203.224.0/19,205.207.134.0/24,205.210.107.0/24,205.210.139.0/24,205.210.171.0/24] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 36"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400035; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [205.236.189.0/24,205.237.88.0/21,206.41.128.0/20,206.41.160.0/19,206.51.29.0/24,206.124.104.0/21,206.125.16.0/20,206.130.188.0/24,206.143.128.0/17,206.183.128.0/19,206.195.224.0/19,206.197.28.0/24,206.197.29.0/24,206.197.77.0/24,206.197.165.0/24,206.209.48.0/20,206.209.80.0/20,206.223.17.0/24,206.224.160.0/19,206.226.0.0/19] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 37"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400036; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [207.90.0.0/18,207.110.64.0/18,207.110.96.0/19,207.110.128.0/18,207.183.64.0/19,207.183.96.0/20,207.183.128.0/19,207.183.192.0/19,207.201.64.0/18,207.228.192.0/20,207.244.0.0/18,208.73.208.0/22,208.90.32.0/21,208.93.4.0/22,209.17.192.0/19,209.66.0.0/18,209.66.128.0/19,209.95.64.0/19,209.95.192.0/19,209.99.128.0/18] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 38"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400037; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) +alert ip [209.145.0.0/19,209.148.16.0/20,209.161.64.0/19,209.161.96.0/20,209.182.64.0/19,209.242.192.0/19,212.162.152.0/22,213.173.36.0/22,213.247.0.0/19,216.179.128.0/17,220.154.0.0/16,221.132.192.0/18,223.0.0.0/15,223.169.0.0/16,223.173.0.0/16,223.254.0.0/16] any -> $HOME_NET any (msg:"ET DROP Spamhaus DROP Listed Traffic Inbound group 39"; reference:url,www.spamhaus.org/drop/drop.lasso; threshold: type limit, track by_src, seconds 3600, count 1; classtype:misc-attack; flowbits:set,ET.Evil; flowbits:set,ET.DROPIP; sid:2400038; rev:2793; metadata:affected_product Any, attack_target Any, deployment Perimeter, tag Dshield, signature_severity Minor, created_at 2010_12_30, updated_at 2021_01_10;) diff --git a/source/networkFirewallAutomation/config/examples/ruleGroups/stateful-domainblock.example.json b/source/networkFirewallAutomation/config/examples/ruleGroups/stateful-domainblock.example.json new file mode 100644 index 0000000..4465c03 --- /dev/null +++ b/source/networkFirewallAutomation/config/examples/ruleGroups/stateful-domainblock.example.json @@ -0,0 +1,31 @@ +{ + "RuleGroupName": "StatefulRulesExample1", + "RuleGroup": { + "RuleVariables": { + "IPSets": { + "HOME_NET": { + "Definition": [ + "10.0.0.0/8", + "172.16.0.0/16" + ] + } + } + }, + "RulesSource": { + "RulesSourceList": { + "TargetTypes": [ + "HTTP_HOST", + "TLS_SNI" + ], + "Targets": [ + "test.example.com", + "test2.example.com" + ], + "GeneratedRulesType": "DENYLIST" + } + } + }, + "Type": "STATEFUL", + "Description": "Stateful Rule", + "Capacity": 100 +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/examples/ruleGroups/stateless-fwd-to-stateful.example.json b/source/networkFirewallAutomation/config/examples/ruleGroups/stateless-fwd-to-stateful.example.json new file mode 100644 index 0000000..3169f0e --- /dev/null +++ b/source/networkFirewallAutomation/config/examples/ruleGroups/stateless-fwd-to-stateful.example.json @@ -0,0 +1,41 @@ +{ + "RuleGroupName": "StatelessExample2", + "RuleGroup": { + "RulesSource": { + "StatelessRulesAndCustomActions": { + "StatelessRules": [ + { + "RuleDefinition": { + "MatchAttributes": { + "Sources": [ + { + "AddressDefinition": "192.0.2.0/8" + } + ], + "Destinations": [ + { + "AddressDefinition": "198.51.100.0/16" + }, + { + "AddressDefinition": "198.52.100.0/16" + } + ], + "Protocols": [ + 6, + 17 + ] + }, + "Actions": [ + "aws:forward_to_sfe" + ] + }, + "Priority": 100 + } + ] + } + } + }, + "Type": "STATELESS", + "Description": "Stateless Rule with Forward to Stateful", + "Capacity": 220 +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/examples/ruleGroups/stateless-pass-action.example.json b/source/networkFirewallAutomation/config/examples/ruleGroups/stateless-pass-action.example.json new file mode 100644 index 0000000..c97b849 --- /dev/null +++ b/source/networkFirewallAutomation/config/examples/ruleGroups/stateless-pass-action.example.json @@ -0,0 +1,68 @@ +{ + "RuleGroupName": "StatelessExample1", + "RuleGroup": { + "RulesSource": { + "StatelessRulesAndCustomActions": { + "StatelessRules": [ + { + "RuleDefinition": { + "MatchAttributes": { + "Sources": [ + { + "AddressDefinition": "192.0.2.0/8" + } + ], + "Destinations": [ + { + "AddressDefinition": "198.51.100.0/16" + } + ], + "SourcePorts": [ + { + "FromPort": 53, + "ToPort": 53 + }, + { + "FromPort": 1001, + "ToPort": 1053 + } + ], + "DestinationPorts": [ + { + "FromPort": 53, + "ToPort": 53 + }, + { + "FromPort": 1001, + "ToPort": 1053 + } + ], + "Protocols": [ + 6 + ], + "TCPFlags": [ + { + "Flags": [ + "SYN" + ], + "Masks": [ + "SYN", + "ACK" + ] + } + ] + }, + "Actions": [ + "aws:pass" + ] + }, + "Priority": 19 + } + ] + } + } + }, + "Type": "STATELESS", + "Description": "Stateless Rule with pass action", + "Capacity": 199 +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/examples/ruleGroups/suricata-rule-reference.json b/source/networkFirewallAutomation/config/examples/ruleGroups/suricata-rule-reference.json new file mode 100644 index 0000000..1175cb4 --- /dev/null +++ b/source/networkFirewallAutomation/config/examples/ruleGroups/suricata-rule-reference.json @@ -0,0 +1,8 @@ + +{ + "RuleGroupName": "suricata-drop-rules", + "Rules": "./ruleGroups/drop.rules", + "Type": "STATEFUL", + "Description": "Suricata rule group", + "Capacity": 100 + } \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/firewallPolicies/firewall-policy-1.json b/source/networkFirewallAutomation/config/firewallPolicies/firewall-policy-1.json new file mode 100644 index 0000000..4a69cbe --- /dev/null +++ b/source/networkFirewallAutomation/config/firewallPolicies/firewall-policy-1.json @@ -0,0 +1,12 @@ +{ + "FirewallPolicyName": "Firewall-Policy-1", + "Description": "Firewall Policy 1", + "FirewallPolicy": { + "StatelessDefaultActions": [ + "aws:forward_to_sfe" + ], + "StatelessFragmentDefaultActions": [ + "aws:pass" + ] + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/config/firewalls/firewall-1.json b/source/networkFirewallAutomation/config/firewalls/firewall-1.json new file mode 100644 index 0000000..782b4f4 --- /dev/null +++ b/source/networkFirewallAutomation/config/firewalls/firewall-1.json @@ -0,0 +1,8 @@ +{ + "FirewallName": "Firewall-1", + "FirewallPolicyArn": "./firewallPolicies/firewall-policy-1.json", + "Description": "Network Firewall 1", + "DeleteProtection": true, + "FirewallPolicyChangeProtection": true, + "SubnetChangeProtection": true +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/index.ts b/source/networkFirewallAutomation/index.ts new file mode 100644 index 0000000..49c47bb --- /dev/null +++ b/source/networkFirewallAutomation/index.ts @@ -0,0 +1,88 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/** + * @description + * AWS Network Firewall Manager Solution + * @author aws-solutions + */ + +import { + EnvironmentProps, + NetworkFirewallManager +} from "./lib/network-firewall-manager" +import { + Ec2EnvironmentProps, + Ec2Manager +} from "./lib/ec2-manager" +import { ConfigReader, ConfigPath } from "./lib/common/configReader/config-reader" +import { Logger, LOG_LEVEL } from "./lib/common/logger" + + +async function firewallManager() { + + // declare environment variables + let envProps: EnvironmentProps = { + vpcId: process.env.VPC_ID, + subnetIds: process.env.SUBNET_IDS, + logDestinationType: process.env.LOG_DESTINATION_TYPE, //S3 or CloudWatchLogs + logDestination: process.env.S3_LOG_BUCKET_NAME !== 'NotConfigured' ? process.env.S3_LOG_BUCKET_NAME : process.env.CLOUDWATCH_LOG_GROUP_NAME, //S3 bucket name or CloudWatchLogs group name + logType: process.env.LOG_TYPE, //ALERT OR FLOW + logRetentionPeriod: process.env.LOG_RETENTION_IN_DAYS, + stackId: process.env.STACK_ID ? process.env.STACK_ID : "" + } + + const transitGatewayAttachmentId = process.env.TRANSIT_GATEWAY_ATTACHMENT_ID ? process.env.TRANSIT_GATEWAY_ATTACHMENT_ID : ""; + const applianceMode = process.env.TRANSIT_GATEWAY_ATTACHMENT_APPLIANCE_MODE ? process.env.TRANSIT_GATEWAY_ATTACHMENT_APPLIANCE_MODE : "enable"; + Ec2Manager.updateTransitGatewayAttachementApplianceMode(transitGatewayAttachmentId, applianceMode); + + + let ec2EnvProps: Ec2EnvironmentProps[] = [ + { + availabilityZone: process.env.VPC_TGW_ATTACHMENT_AZ_1, + routeTableId: process.env.VPC_TGW_ATTACHMENT_ROUTE_TABLE_ID_1 + }, + { + availabilityZone: process.env.VPC_TGW_ATTACHMENT_AZ_2, + routeTableId: process.env.VPC_TGW_ATTACHMENT_ROUTE_TABLE_ID_2 + }] + + try { + const currentPath = process.cwd() + const directoryPath = currentPath.concat(ConfigPath.firewallDirectory) + + const fileHandler = new ConfigReader() + const firewallFiles = fileHandler.getJSONFileNames(directoryPath) + + for (let filePath of firewallFiles) { + + Logger.log(LOG_LEVEL.INFO, `Processing ${filePath}`) + let firewallObject = fileHandler.convertFileToObject(filePath) + Logger.log(LOG_LEVEL.INFO, firewallObject) + let firewallMgr = new NetworkFirewallManager(envProps, firewallObject, fileHandler) + const syncStates = await firewallMgr.firewallOperations() + Logger.log(LOG_LEVEL.INFO, syncStates) + Logger.log(LOG_LEVEL.INFO, `Creating route to firewall endpoint.`) + if (syncStates) { + const ec2Mgr = new Ec2Manager(ec2EnvProps, syncStates) + await ec2Mgr.routeTableOperations() + } + } + } catch (error) { + Logger.log(LOG_LEVEL.ERROR, `Failed to deploy/update Network Firewall`, error) + process.exit(1) + } +} + +// Initiating Network Firewall Manager Solution +firewallManager() \ No newline at end of file diff --git a/source/networkFirewallAutomation/lib/common/configReader/config-reader.ts b/source/networkFirewallAutomation/lib/common/configReader/config-reader.ts new file mode 100644 index 0000000..488ae6d --- /dev/null +++ b/source/networkFirewallAutomation/lib/common/configReader/config-reader.ts @@ -0,0 +1,63 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +import * as fs from 'fs' +import * as path from 'path' +import { Logger, LOG_LEVEL } from '../logger' + +export enum ConfigPath { + firewallDirectory = '/firewalls' +} +/** + * @description This class reads the json files and return file objects + */ +export class ConfigReader { + + /** + * @description This method will return the json file names in the path. + * @param directoryPath string value of the file system path. + * @returns Array of file names. + */ + getJSONFileNames(directoryPath: string): string[] { + Logger.log(LOG_LEVEL.DEBUG, `Config directory path: ${directoryPath}`) + return fs.readdirSync(directoryPath) + .filter((name: any) => path.extname(name) === '.json') + .map((name: any) => (path.join(directoryPath, name))) + } + + /** + * This method will read the file contents and attempt to convert the file content into JSON object. + * @returns JSON object of the file content. + * @param filePath string value of absolute file path. + */ + convertFileToObject(filePath: string): any { + Logger.log(LOG_LEVEL.DEBUG, `Returning object for file: ${filePath}`) + return JSON.parse(fs.readFileSync(filePath).toString()) + } + + /** + * This method will read the file contents and attempt to convert the file content into a string. Method will return an empty string + * if the file path is incorrect or invalid. + * @returns String representation of the file content. + * @param filePath string value of absolute file path. + */ + copyFileContentToString(filePath: string): any { + Logger.log(LOG_LEVEL.DEBUG, `Returning string content for file: ${filePath}`) + try { + return fs.readFileSync(filePath).toString() + } catch(error) { + Logger.log(LOG_LEVEL.DEBUG, `Error converting the file content to string:`, error) + return ""; + } + } + +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/lib/common/firewall-config-validation.ts b/source/networkFirewallAutomation/lib/common/firewall-config-validation.ts new file mode 100644 index 0000000..15be7fa --- /dev/null +++ b/source/networkFirewallAutomation/lib/common/firewall-config-validation.ts @@ -0,0 +1,214 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { NetworkFirewall } from "aws-sdk"; +import { Logger, LOG_LEVEL } from "./logger"; +import { ConfigReader, ConfigPath } from "./configReader/config-reader"; +import { MetricsManager, NetworkFirewallMetrics } from "./send-metrics"; + +interface InvalidConfigFiles { + path: string; + referencedInFile?: any; + error?: any; +} + +export class FirewallConfigValidation { + + private invalidFiles: InvalidConfigFiles[]; + private service: NetworkFirewall; + private fileHandler: ConfigReader; + + constructor() { + this.invalidFiles = [] + this.service = new NetworkFirewall() + this.fileHandler = new ConfigReader() + } + + getInvalidFiles() { + return this.invalidFiles + } + + /** + * This method will validate all the files in starting with firewall, firewall policy and rule groups, all the invalid + * files will be output to the console and an error is thrown, + * if there no invalid files the validation will exit without any error. + * @param rootDir optional if the value is not provided the path configured in the ConfigPath is taken as directory. + */ + async execute(rootDir?: string) { + const metrics: NetworkFirewallMetrics = { + numberOfFirewalls: 0, + numberOfPolicies: 0, + numberOfStatefulRuleGroups: 0, + numberOfStatelessRuleGroups: 0, + numberOfSuricataRules: 0 + } + Logger.log(LOG_LEVEL.INFO, `Starting firewall config validation`) + try { + const currentPath = process.cwd() + let directoryPath; + if (rootDir) { + directoryPath = currentPath.concat(rootDir) + } else { + directoryPath = currentPath.concat(ConfigPath.firewallDirectory) + } + Logger.log(LOG_LEVEL.INFO, `Config file path ${directoryPath}`) + const firewallFiles = this.fileHandler.getJSONFileNames(directoryPath) + metrics.numberOfFirewalls = firewallFiles.length + + for (let firewallFile of firewallFiles) { + Logger.log(LOG_LEVEL.INFO, `Validating the file paths for the firewall file named: ${firewallFile}`) + let firewall: NetworkFirewall.Types.CreateFirewallRequest = this.fileHandler.convertFileToObject(firewallFile) + + this.validateFirewallFile(firewall) + + let firewallPolicy: NetworkFirewall.Types.CreateFirewallPolicyRequest; + + //verify firewall policy. + try { + firewallPolicy = this.fileHandler.convertFileToObject(firewall.FirewallPolicyArn) + metrics.numberOfPolicies += 1 + await this.validateFirewallPolicyFile(firewallPolicy, firewall.FirewallPolicyArn) + } catch (error) { + Logger.log(LOG_LEVEL.INFO, `Failed to validate the firewall policy`) + this.invalidFiles.push({ + path: firewall.FirewallPolicyArn, + referencedInFile: firewall.FirewallPolicyArn, + error: "The file in the attribute path is not available in the configuration." + }) + break; + } + + //loop through all the stateful rule groups and verify if the files compile to a valid json object. + if (firewallPolicy.FirewallPolicy.StatefulRuleGroupReferences) { + metrics.numberOfStatefulRuleGroups += firewallPolicy.FirewallPolicy.StatefulRuleGroupReferences.length + Logger.log(LOG_LEVEL.DEBUG, `Firewall Policy StatefulRuleGroupReferences`, firewallPolicy.FirewallPolicy.StatefulRuleGroupReferences) + for (let statefulRuleGroup of firewallPolicy.FirewallPolicy.StatefulRuleGroupReferences) { + try { + const ruleGroup: NetworkFirewall.Types.CreateRuleGroupRequest = this.fileHandler.convertFileToObject(statefulRuleGroup.ResourceArn); + if (ruleGroup.Rules) { + metrics.numberOfSuricataRules += 1; + } + await this.validateRuleGroupFile(ruleGroup, statefulRuleGroup.ResourceArn) + } catch (error) { + this.invalidFiles.push({ + path: statefulRuleGroup.ResourceArn, + referencedInFile: firewall.FirewallPolicyArn, + error: "The file in the attribute path is not available in the configuration." + }) + } + } + } + //loop through all the stateless rule groups and verify if the files compile to a valid json object. + if (firewallPolicy.FirewallPolicy.StatelessRuleGroupReferences) { + metrics.numberOfStatelessRuleGroups += firewallPolicy.FirewallPolicy.StatelessRuleGroupReferences.length + Logger.log(LOG_LEVEL.DEBUG, `Firewall Policy StatelessRuleGroupReferences`, firewallPolicy.FirewallPolicy.StatelessRuleGroupReferences) + for (let statelessRuleGroup of firewallPolicy.FirewallPolicy.StatelessRuleGroupReferences) { + try { + const ruleGroup = this.fileHandler.convertFileToObject(statelessRuleGroup.ResourceArn) + await this.validateRuleGroupFile(ruleGroup, statelessRuleGroup.ResourceArn) + } catch (error) { + this.invalidFiles.push({ + path: statelessRuleGroup.ResourceArn, + referencedInFile: firewall.FirewallPolicyArn, + error: "The file in the attribute path is not available in the configuration." + }) + } + } + } + } + + } catch (error) { + Logger.log(LOG_LEVEL.ERROR, error) + throw new Error("Validation failed."); + } finally { + Logger.log(LOG_LEVEL.INFO, `Number of invalid files: ${this.invalidFiles.length}`) + Logger.log(LOG_LEVEL.INFO, `-----------INVALID FILES START-----------`) + this.getInvalidFiles().forEach((invalidFile) => { + Logger.log(LOG_LEVEL.ERROR, invalidFile) + }) + Logger.log(LOG_LEVEL.INFO, `-----------INVALID FILES END--------------`) + if (this.invalidFiles.length > 0) { + const error = "Validation failed." + Logger.log(LOG_LEVEL.ERROR, error) + throw error + } + Logger.log(LOG_LEVEL.DEBUG, `Send metrics`, metrics) + MetricsManager.sendMetrics(metrics) + } + } + + async validateFirewallPolicyFile(firewallPolicy: NetworkFirewall.Types.CreateFirewallPolicyRequest, path: string) { + firewallPolicy.DryRun = true; + let response; + try { + response = await this.service.createFirewallPolicy(firewallPolicy).promise() + } catch (error) { + const errorCode: string = error["code"] + Logger.log(LOG_LEVEL.DEBUG, `Error response from the create firewall policy dry run API`, error) + if (errorCode === "MultipleValidationErrors" || errorCode === "UnexpectedParameter") { + this.invalidFiles.push({ + path: path, + error: error["message"] + }) + } + } + Logger.log(LOG_LEVEL.DEBUG, `Response from the create firewall policy dry run API`, response) + } + async validateRuleGroupFile(ruleGroup: NetworkFirewall.Types.CreateRuleGroupRequest, path: string) { + //add code to check if this rule source is provided or rules file is being provided + if (ruleGroup.Rules && ruleGroup.RuleGroup) { + Logger.log(LOG_LEVEL.DEBUG, `Rule Group file has both Rules and RuleGroup fields.`, ruleGroup) + this.invalidFiles.push({ + path: path, + error: "Both RuleGroup and Rules have data, You must provide either the rule group setting or a Rules setting, but not both. " + }) + return; + } else if (ruleGroup.Rules) { + const ruleString = this.fileHandler.copyFileContentToString(ruleGroup.Rules) + if (!ruleString) { + ruleGroup.Rules = ruleString + this.invalidFiles.push({ + path: path, + error: "Rules attribute has invalid file path. " + ruleGroup.Rules + }) + } + Logger.log(LOG_LEVEL.DEBUG, `Rule Group file has both Rules and RuleGroup fields.`, ruleGroup.Rules) + } + + ruleGroup.DryRun = true; + let response; + try { + response = await this.service.createRuleGroup(ruleGroup).promise(); + } catch(error) { + Logger.log(LOG_LEVEL.DEBUG, `Error response from the create rule group dry run API`, error) + const errorCode: string = error["code"] + if (errorCode === "MultipleValidationErrors" || errorCode === "UnexpectedParameter") { + this.invalidFiles.push({ + path: path, + error: error["message"] + }) + } + } + Logger.log(LOG_LEVEL.DEBUG, `Response from the create rule group dry run API`, response) + } + + validateFirewallFile(firewall: NetworkFirewall.Types.CreateFirewallRequest) { + if (!firewall.FirewallName || !firewall.FirewallPolicyArn) { + this.invalidFiles.push({ + path: firewall.FirewallName, + referencedInFile: firewall.FirewallName, + error: "FirewallName and FirewallPolicyArn are required in the firewall." + }) + } + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/lib/common/logger.ts b/source/networkFirewallAutomation/lib/common/logger.ts new file mode 100644 index 0000000..f1c69f9 --- /dev/null +++ b/source/networkFirewallAutomation/lib/common/logger.ts @@ -0,0 +1,38 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +export enum LOG_LEVEL { + "ERROR", + "WARN", + "INFO", + "DEBUG" +} + +export class Logger { + + private static readonly CONFIGURED_LOG_LEVEL = process.env.LOG_LEVEL && Object.values(LOG_LEVEL).indexOf(process.env.LOG_LEVEL.toUpperCase()) != -1 ? Object.values(LOG_LEVEL).indexOf(process.env.LOG_LEVEL.toUpperCase()) : LOG_LEVEL.ERROR; + + constructor() { } + + static log(log_level: LOG_LEVEL, message: any, object?: any) { + if (log_level <= this.CONFIGURED_LOG_LEVEL) { + let currentDateTime = new Date() + let formatted_date = `${currentDateTime.getFullYear()}-${(currentDateTime.getMinutes()-1)}-${currentDateTime.getDate()} ${currentDateTime.getHours()}:${currentDateTime.getMinutes()}:${currentDateTime.getSeconds()}` + let log_message = `${formatted_date} : ${JSON.stringify(message, null, 2)}` + if (object) { + log_message = `${formatted_date} : ${JSON.stringify(message, null, 2)} : ${JSON.stringify(object, null, 2)}` + } + console.log(log_message) + } + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/lib/common/send-metrics.ts b/source/networkFirewallAutomation/lib/common/send-metrics.ts new file mode 100644 index 0000000..527e357 --- /dev/null +++ b/source/networkFirewallAutomation/lib/common/send-metrics.ts @@ -0,0 +1,80 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ +import { v4 as uuidv4 } from "uuid" +import { SSM } from "aws-sdk" +import axios from "axios" +import { Logger, LOG_LEVEL } from "./logger" + +export interface NetworkFirewallMetrics { + numberOfFirewalls: number, + numberOfStatefulRuleGroups: number, + numberOfStatelessRuleGroups: number, + numberOfPolicies: number, + numberOfSuricataRules: number, + logType?: string + logDestinationType?: string +} + +export class MetricsManager { + + private constructor() { } + + static async sendMetrics(data: NetworkFirewallMetrics) { + const ssmParameterForUUID = process.env.SSM_PARAM_FOR_UUID ? process.env.SSM_PARAM_FOR_UUID : "network-firewall-solution-uuid" + const stackId = process.env.STACK_ID ? process.env.STACK_ID.slice(process.env.STACK_ID.length - 36) : "" + const sendAnonymousMetrics = process.env.SEND_ANONYMOUS_METRICS ? process.env.SEND_ANONYMOUS_METRICS : "No" + let uuid = "" + const ssmUUIDKey = `${ssmParameterForUUID}-${stackId}` + try { + if (sendAnonymousMetrics.toUpperCase() === "YES") { + let ssmInstance = new SSM(); + let ssmGetParamResponse; + try { + ssmGetParamResponse = await ssmInstance.getParameter({ + Name: ssmUUIDKey, + }).promise(); + uuid = ssmGetParamResponse.Parameter?.Value ? ssmGetParamResponse.Parameter?.Value : uuidv4(); + } catch (error) { + if (error["code"] = "ParameterNotFound") { + uuid = uuidv4(); + await ssmInstance.putParameter({ + Name: ssmUUIDKey, + Value: uuid, + Type: "String" + }).promise(); + } + } + Logger.log(LOG_LEVEL.DEBUG, "uuid: ", uuid) + const metricsUrl: string = process.env.METRICS_URL ? process.env.METRICS_URL : "" + const solutionId: string | undefined = process.env.SOLUTION_ID + const timestamp = (new Date()).toISOString() + data.logDestinationType = process.env.LOG_DESTINATION_TYPE + data.logType = process.env.LOG_TYPE + const metrics_data = { + 'Solution': solutionId, + 'TimeStamp': timestamp, + 'UUID': uuid, + 'Data': data + } + Logger.log(LOG_LEVEL.DEBUG, "metrics data: ", metrics_data) + const response = await axios.post(metricsUrl, metrics_data, { + headers: { + 'Content-Type': 'application/json', + 'Content-Length': JSON.stringify(data).length + } + }) + Logger.log(LOG_LEVEL.DEBUG, 'Response: ', response) + } + } catch (error) { } + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/lib/common/stringUtils.ts b/source/networkFirewallAutomation/lib/common/stringUtils.ts new file mode 100644 index 0000000..e2fcc6b --- /dev/null +++ b/source/networkFirewallAutomation/lib/common/stringUtils.ts @@ -0,0 +1,55 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { Logger, LOG_LEVEL } from './logger' + +export enum Name { + maxCharacters = 128, + delimiter= '-' +} + +/** + * @description This class performs string manipulation operations + */ +export class StringUtils { + + constructor(readonly stackId: string) { + } + + + /** + * @description This method will return name of the resource with parsed + * stack id and validates the max character allowed + * @param resourceName + * @returns modified resource name. + */ + getUniqueResourceName(resourceName: string) { + Logger.log(LOG_LEVEL.DEBUG, `Resource name input: ${resourceName}`) + if (this.stackId) { + const splitStackId = this.stackId.split("-").pop() + let customName = resourceName + Name.delimiter + splitStackId + if (splitStackId && customName.length > Name.maxCharacters) { + const sliceString = Name.maxCharacters - (splitStackId.length + Name.delimiter.length) + Logger.log(LOG_LEVEL.INFO, `Modified name is larger than 128 characters, trimming the resource name and using only first ${sliceString.toString()} characters from the name.`) + const trimmedResourceName = resourceName.substring(0, sliceString) + customName = trimmedResourceName + Name.delimiter + splitStackId + } + Logger.log(LOG_LEVEL.DEBUG, `Returning Custom name : ${resourceName}`) + return customName + } + else { + throw Error("The stack id environment variable is undefined in the" + + " CodeBuild stage environment variables.") + } + } +} \ No newline at end of file diff --git a/source/networkFirewallAutomation/lib/ec2-manager.ts b/source/networkFirewallAutomation/lib/ec2-manager.ts new file mode 100644 index 0000000..5d816e4 --- /dev/null +++ b/source/networkFirewallAutomation/lib/ec2-manager.ts @@ -0,0 +1,170 @@ +/** + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import { EC2, NetworkFirewall } from "aws-sdk" +import { Ec2Service } from "./service/ec2-service" +import { LOG_LEVEL, Logger } from "./common/logger" + +export interface Ec2EnvironmentProps { + availabilityZone: string | undefined, + routeTableId: string | undefined +} + +export enum Route { + default = '0.0.0.0/0', + active = 'active' +} + +type routeStatus = { + VpcEndpointId: string | undefined, + RouteTableId: string, + DefaultRouteCreated: boolean +} + +/** + * @description This class contains all the methods to + * perform CRUD operations for the VPC route to Network Firewall. + */ +export class Ec2Manager { + + private service: Ec2Service + private vpcEndpoint: string | undefined + + constructor(public envProps: Ec2EnvironmentProps[], + public firewallSyncStates: NetworkFirewall.SyncStates) { + this.service = new Ec2Service() + } + + /** this method will check if route exists, if not will start the process to + * create the route, If route exists no action required. If any of the VPC + * endpoint is not in READY status, throw an error. + */ + async routeTableOperations(): Promise