├── .gitignore ├── .pre-commit-config.yaml ├── modules ├── sns_email_subscription │ ├── output.tf │ ├── variables.tf │ ├── README.md │ └── main.tf ├── cwe │ ├── output.tf │ ├── main.tf │ ├── variables.tf │ └── README.md ├── sns_cross_account_sqs │ ├── main.tf │ ├── variables.tf │ └── README.md ├── reflex_kms_key │ ├── README.md │ ├── output.tf │ └── main.tf ├── sqs_lambda │ ├── modules │ │ └── iam_assume_role │ │ │ ├── variables.tf │ │ │ ├── main.tf │ │ │ └── README.md │ ├── README.md │ ├── variables.tf │ └── main.tf └── sns_cross_region_sqs │ ├── variables.tf │ ├── README.md │ └── main.tf ├── .github ├── .releaserc.json └── workflows │ ├── branch_flow.yml │ └── release.yml ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | terraform.tfstate 3 | *.tfstate* 4 | terraform.tfvars 5 | .idea -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | - repo: git://github.com/antonbabenko/pre-commit-terraform 2 | rev: v1.19.0 3 | hooks: 4 | - id: terraform_fmt 5 | - id: terraform_docs 6 | -------------------------------------------------------------------------------- /modules/sns_email_subscription/output.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "SNS Topic ARN" 3 | value = element(concat(aws_cloudformation_stack.sns_topic.*.outputs.ARN, [""]), 0) 4 | } 5 | -------------------------------------------------------------------------------- /.github/.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ["@semantic-release/commit-analyzer"], 4 | ["@semantic-release/release-notes-generator"], 5 | ["@semantic-release/github"] 6 | ], 7 | "preset": "eslint" 8 | } 9 | -------------------------------------------------------------------------------- /modules/cwe/output.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "Event Rule ID" 3 | value = element(concat(aws_cloudwatch_event_rule.cwe_rule.*.id, [""]), 0) 4 | } 5 | 6 | output "arn" { 7 | description = "Event Rule Arn" 8 | value = element(concat(aws_cloudwatch_event_rule.cwe_rule.*.arn, [""]), 0) 9 | } 10 | 11 | -------------------------------------------------------------------------------- /modules/cwe/main.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * CWE: A reusable module for creating and passing cloudwatch events. 3 | */ 4 | resource "aws_cloudwatch_event_rule" "cwe_rule" { 5 | name = var.name 6 | description = var.description 7 | event_pattern = var.event_pattern 8 | tags = { 9 | Reflex = "true" 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /modules/cwe/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "A name for the Cloudwatch Event." 3 | type = string 4 | } 5 | 6 | variable "description" { 7 | description = "A description for our CWE rule" 8 | type = string 9 | } 10 | 11 | variable "event_pattern" { 12 | description = "Event pattern that details the events to listen for." 13 | type = string 14 | } 15 | -------------------------------------------------------------------------------- /modules/sns_cross_account_sqs/main.tf: -------------------------------------------------------------------------------- 1 | module "cross_account_event_forwarding" { 2 | source = "../sns_cross_region_sqs" 3 | kms_key_id = var.kms_key_id 4 | cloudwatch_event_rule_id = var.cloudwatch_event_rule_id 5 | central_region = var.central_region 6 | central_queue_name = var.central_queue_name 7 | parent_account = var.parent_account 8 | } 9 | -------------------------------------------------------------------------------- /modules/reflex_kms_key/README.md: -------------------------------------------------------------------------------- 1 | Reflex KMS Key: Creates a KMS key that will be used by reflex infrastructure for encryption. 2 | 3 | ## Providers 4 | 5 | | Name | Version | 6 | |------|---------| 7 | | aws | n/a | 8 | 9 | ## Inputs 10 | 11 | No input. 12 | 13 | ## Outputs 14 | 15 | | Name | Description | 16 | |------|-------------| 17 | | alias | Reflex KMS Alias Arn | 18 | | key\_arn | Reflex KMS Key Arn | 19 | | key\_id | Reflex KMS Key Id | 20 | 21 | -------------------------------------------------------------------------------- /modules/sqs_lambda/modules/iam_assume_role/variables.tf: -------------------------------------------------------------------------------- 1 | variable "lambda_execution_role_arn" { 2 | description = "Arn for lambda execution role." 3 | type = string 4 | } 5 | 6 | variable "function_name" { 7 | description = "Clean name for Lambda function" 8 | type = string 9 | } 10 | 11 | variable "custom_lambda_policy" { 12 | description = "Lambda policy specific to invoked lambda" 13 | type = string 14 | default = null 15 | } 16 | 17 | -------------------------------------------------------------------------------- /modules/reflex_kms_key/output.tf: -------------------------------------------------------------------------------- 1 | output "key_id" { 2 | description = "Reflex KMS Key Id" 3 | value = element(concat(aws_kms_key.reflex_key.*.key_id, [""]), 0) 4 | } 5 | 6 | output "key_arn" { 7 | description = "Reflex KMS Key Arn" 8 | value = element(concat(aws_kms_key.reflex_key.*.arn, [""]), 0) 9 | } 10 | 11 | output "alias" { 12 | description = "Reflex KMS Alias Arn" 13 | value = element(concat(aws_kms_alias.reflex_alias.*.arn, [""]), 0) 14 | } 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/branch_flow.yml: -------------------------------------------------------------------------------- 1 | name: reflex-engine-branch 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - master 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python 3.8 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.8 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install checkov 21 | - name: Lint with checkov 22 | run: | 23 | checkov --skip-check CKV_AWS_50 -d modules 24 | -------------------------------------------------------------------------------- /modules/sns_cross_account_sqs/variables.tf: -------------------------------------------------------------------------------- 1 | variable "kms_key_id" { 2 | description = "Key ID of reflex KMS key" 3 | type = string 4 | } 5 | 6 | variable "cloudwatch_event_rule_id" { 7 | description = "Easy name for our CWE rule" 8 | type = string 9 | } 10 | 11 | variable "central_region" { 12 | description = "Central region to forward events to" 13 | type = string 14 | } 15 | 16 | variable "central_queue_name" { 17 | description = "Camel case name of queue found in central region" 18 | type = string 19 | } 20 | 21 | variable "parent_account" { 22 | description = "Account id that we will forward events to" 23 | type = string 24 | } 25 | -------------------------------------------------------------------------------- /modules/cwe/README.md: -------------------------------------------------------------------------------- 1 | CWE: A reusable module for creating and passing cloudwatch events. 2 | 3 | ## Providers 4 | 5 | | Name | Version | 6 | |------|---------| 7 | | aws | n/a | 8 | 9 | ## Inputs 10 | 11 | | Name | Description | Type | Default | Required | 12 | |------|-------------|------|---------|:--------:| 13 | | description | A description for our CWE rule | `string` | n/a | yes | 14 | | event\_pattern | Event pattern that details the events to listen for. | `string` | n/a | yes | 15 | | name | A name for the Cloudwatch Event. | `string` | n/a | yes | 16 | 17 | ## Outputs 18 | 19 | | Name | Description | 20 | |------|-------------| 21 | | arn | Event Rule Arn | 22 | | id | Event Rule ID | 23 | 24 | -------------------------------------------------------------------------------- /modules/sns_cross_region_sqs/variables.tf: -------------------------------------------------------------------------------- 1 | variable "kms_key_id" { 2 | description = "Key ID of reflex KMS key" 3 | type = string 4 | } 5 | 6 | variable "cloudwatch_event_rule_id" { 7 | description = "Easy name for our CWE rule" 8 | type = string 9 | } 10 | 11 | variable "central_region" { 12 | description = "Central region to forward events to" 13 | type = string 14 | } 15 | 16 | variable "central_queue_name" { 17 | description = "Camel case name of queue found in central region" 18 | type = string 19 | } 20 | 21 | variable "parent_account" { 22 | description = "Account id that we will forward events to" 23 | type = string 24 | default = null 25 | } 26 | -------------------------------------------------------------------------------- /modules/sns_cross_account_sqs/README.md: -------------------------------------------------------------------------------- 1 | sns\_cross\_region\_sqs: module to create forwarder infrastructure using SNS topic publishing to a central SQS queue. 2 | 3 | ## Providers 4 | 5 | | Name | Version | 6 | |------|---------| 7 | | aws | n/a | 8 | 9 | ## Inputs 10 | 11 | | Name | Description | Type | Default | Required | 12 | |------|-------------|------|---------|:--------:| 13 | | central\_queue\_name | Camel case name of queue found in central region | `string` | n/a | yes | 14 | | central\_region | Central region to forward events to | `string` | n/a | yes | 15 | | cloudwatch\_event\_rule\_id | Easy name for our CWE rule | `string` | n/a | yes | 16 | | kms\_key\_id | Key ID of reflex KMS key | `string` | n/a | yes | 17 | 18 | ## Outputs 19 | 20 | No output. 21 | 22 | -------------------------------------------------------------------------------- /modules/sns_cross_region_sqs/README.md: -------------------------------------------------------------------------------- 1 | sns\_cross\_region\_sqs: module to create forwarder infrastructure using SNS topic publishing to a central SQS queue. 2 | 3 | ## Providers 4 | 5 | | Name | Version | 6 | |------|---------| 7 | | aws | n/a | 8 | 9 | ## Inputs 10 | 11 | | Name | Description | Type | Default | Required | 12 | |------|-------------|------|---------|:--------:| 13 | | central\_queue\_name | Camel case name of queue found in central region | `string` | n/a | yes | 14 | | central\_region | Central region to forward events to | `string` | n/a | yes | 15 | | cloudwatch\_event\_rule\_id | Easy name for our CWE rule | `string` | n/a | yes | 16 | | kms\_key\_id | Key ID of reflex KMS key | `string` | n/a | yes | 17 | 18 | ## Outputs 19 | 20 | No output. 21 | 22 | -------------------------------------------------------------------------------- /modules/sqs_lambda/modules/iam_assume_role/main.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * lambda_iam: Reflex module to create AssumeRole for lambdas to use 3 | */ 4 | 5 | resource "aws_iam_role" "assume_role" { 6 | name = "Reflex${var.function_name}LambdaAssume" 7 | 8 | tags = { 9 | Reflex = "true" 10 | } 11 | 12 | assume_role_policy = <[
"events.amazonaws.com"
] | no | 15 | | sns\_actions | A list of actions allowed | `list(string)` |
[
"SNS:Publish"
]
| no | 16 | | stack\_name | A name for the SNS Topic Stack | `string` | n/a | yes | 17 | | topic\_name | A name for the SNS Topic | `string` | n/a | yes | 18 | 19 | ## Outputs 20 | 21 | | Name | Description | 22 | |------|-------------| 23 | | arn | SNS Topic ARN | 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # reflex 2 | ![Reflex](https://avatars3.githubusercontent.com/u/59374073?s=200&v=4) 3 | 4 | Reflex enforces security best practices in cloud environments through event-driven automation. Get started by visiting [our documentation](https://docs.reflexivesecurity.com/). 5 | 6 | ## reflex-engine 7 | These are the engine terraform modules that drive the reflex event driven detective and remediative infrastructure for your cloud environment. 8 | 9 | ## Overview 10 | reflex provides an infrastructure-as-code first approach to creating detective controls in your AWS environment. 11 | 12 | The modules in this repository are meant to be consumed by other detective rules that are specified as part of the reflex system and by custom rules made by end users. For example, the cwe_sns_email module can be used to very simply forward a specified CloudWatch Event to an SNS Topic that has an email subscription. 13 | 14 | ## More Information 15 | 16 | - https://reflexivesecurity.com/ 17 | - https://docs.reflexivesecurity.com/ 18 | -------------------------------------------------------------------------------- /modules/sqs_lambda/modules/iam_assume_role/README.md: -------------------------------------------------------------------------------- 1 | lambda: Reflex module to create lambda function infrastructure for processing events. 2 | 3 | ## Providers 4 | 5 | | Name | Version | 6 | |------|---------| 7 | | archive | n/a | 8 | | aws | n/a | 9 | | null | n/a | 10 | 11 | ## Inputs 12 | 13 | | Name | Description | Type | Default | Required | 14 | |------|-------------|------|---------|:--------:| 15 | | custom\_lambda\_policy | Lambda policy specific to invoked lambda | `string` | `null` | no | 16 | | environment\_variable\_map | Map of environment variables for Lambda | `map(string)` | n/a | yes | 17 | | function\_name | Clean name for Lambda function | `string` | n/a | yes | 18 | | handler | Handler location for lambda function | `string` | n/a | yes | 19 | | kms\_key\_id | KMS Key Id to be used with CloudWatch Logs | `string` | n/a | yes | 20 | | lambda\_runtime | Language runtime for lambda function | `string` | n/a | yes | 21 | | sns\_topic\_arn | Topic arn for deployed notification topic | `string` | n/a | yes | 22 | | source\_code\_dir | Directory holding Lambda source code | `string` | n/a | yes | 23 | | sqs\_queue\_arn | Arn of resource for sqs IAM permissions | `string` | n/a | yes | 24 | 25 | ## Outputs 26 | 27 | | Name | Description | 28 | |------|-------------| 29 | | arn | Lambda Arn | 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python 3.8 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.8 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install checkov 21 | - name: Lint with checkov 22 | run: | 23 | checkov --skip-check CKV_AWS_50 -d modules 24 | 25 | deploy: 26 | needs: lint 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v2 30 | - name: Set up node 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: '12.x' 34 | - name: Install dependencies 35 | run: | 36 | npm install @semantic-release/commit-analyzer \ 37 | @semantic-release/release-notes-generator \ 38 | @semantic-release/github \ 39 | conventional-changelog-eslint \ 40 | semantic-release 41 | - name: Create release 42 | run: | 43 | npx semantic-release -e ./.github/.releaserc.json 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.CLOUDMITIGATOR_GITHUB_PAT }} 46 | -------------------------------------------------------------------------------- /modules/sqs_lambda/README.md: -------------------------------------------------------------------------------- 1 | sqs\_lambda: module to generate the targeted sqs queue to lambda ingestion of event payloads 2 | 3 | ## Providers 4 | 5 | | Name | Version | 6 | |------|---------| 7 | | aws | n/a | 8 | 9 | ## Inputs 10 | 11 | | Name | Description | Type | Default | Required | 12 | |------|-------------|------|---------|:--------:| 13 | | cloudwatch\_event\_rule\_arn | Arn of previously generated event rule | `string` | n/a | yes | 14 | | cloudwatch\_event\_rule\_id | Cloudwatch event rule name | `string` | n/a | yes | 15 | | custom\_lambda\_policy | Lambda policy specific to invoked lambda | `string` | `null` | no | 16 | | delay\_seconds | Seconds to delay processing of message in sqs queue | `number` | `0` | no | 17 | | environment\_variable\_map | Map of environment variables for Lambda | `map(string)` | n/a | yes | 18 | | function\_name | Clean name for Lambda function | `string` | n/a | yes | 19 | | handler | Handler location for lambda function | `string` | n/a | yes | 20 | | lambda\_runtime | Language runtime for lambda function | `string` | n/a | yes | 21 | | queue\_name | Name of sqs queue | `string` | n/a | yes | 22 | | sns\_topic\_arn | SNS Topic arn for lambda access to publish messages. | `string` | n/a | yes | 23 | | source\_code\_dir | Directory holding Lambda source code | `string` | n/a | yes | 24 | | sqs\_kms\_key\_id | KMS Key Id to be used with SQS | `string` | n/a | yes | 25 | | target\_id | Target ID | `string` | n/a | yes | 26 | 27 | ## Outputs 28 | 29 | No output. 30 | 31 | -------------------------------------------------------------------------------- /modules/sqs_lambda/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cloudwatch_event_rule_id" { 2 | description = "Cloudwatch event rule name" 3 | type = string 4 | } 5 | 6 | variable "cloudwatch_event_rule_arn" { 7 | description = "Arn of previously generated event rule" 8 | type = string 9 | } 10 | 11 | variable "target_id" { 12 | description = "Target ID" 13 | type = string 14 | } 15 | 16 | variable "package_location" { 17 | description = "Path for the Lambda deployment package" 18 | type = string 19 | } 20 | 21 | variable "function_name" { 22 | description = "Clean name for Lambda function" 23 | type = string 24 | } 25 | 26 | variable "handler" { 27 | description = "Handler location for lambda function" 28 | type = string 29 | } 30 | 31 | variable "lambda_runtime" { 32 | description = "Language runtime for lambda function" 33 | type = string 34 | } 35 | 36 | variable "environment_variable_map" { 37 | description = "Map of environment variables for Lambda" 38 | type = map(string) 39 | } 40 | 41 | variable "queue_name" { 42 | description = "Name of sqs queue" 43 | type = string 44 | } 45 | 46 | variable "delay_seconds" { 47 | description = "Seconds to delay processing of message in sqs queue" 48 | type = number 49 | default = 0 50 | } 51 | 52 | variable "custom_lambda_policy" { 53 | description = "Lambda policy specific to invoked lambda" 54 | type = string 55 | default = null 56 | } 57 | 58 | variable "sns_topic_arn" { 59 | description = "SNS Topic arn for lambda access to publish messages." 60 | type = string 61 | } 62 | 63 | variable "sqs_kms_key_id" { 64 | description = "KMS Key Id to be used with SQS" 65 | type = string 66 | } 67 | 68 | variable "lambda_timeout" { 69 | description = "Lambda timeout as a configuration setting" 70 | type = number 71 | default = 60 72 | } 73 | 74 | variable "visibility_timeout_seconds" { 75 | description = "Time in seconds that message is hidden from other consumers." 76 | type = number 77 | default = 60 78 | } 79 | 80 | variable "max_receive_count" { 81 | description = "Maxium number of retries of event before event is considered an error" 82 | type = number 83 | default = 3 84 | } 85 | -------------------------------------------------------------------------------- /modules/sns_cross_region_sqs/main.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * sns_cross_region_sqs: module to create forwarder infrastructure using SNS topic publishing to a central SQS queue. 3 | */ 4 | data "aws_caller_identity" "current" {} 5 | 6 | resource "aws_sns_topic" "forwarder_topic" { 7 | name = "Forwarder-${var.cloudwatch_event_rule_id}" 8 | kms_master_key_id = var.kms_key_id 9 | tags = { 10 | Reflex = "true" 11 | } 12 | } 13 | 14 | resource "aws_sns_topic_policy" "events_policy" { 15 | arn = aws_sns_topic.forwarder_topic.arn 16 | 17 | policy = "${data.aws_iam_policy_document.sns_topic_policy.json}" 18 | } 19 | 20 | data "aws_iam_policy_document" "sns_topic_policy" { 21 | policy_id = "__default_policy_ID" 22 | 23 | statement { 24 | actions = [ 25 | "SNS:Subscribe", 26 | "SNS:SetTopicAttributes", 27 | "SNS:RemovePermission", 28 | "SNS:Receive", 29 | "SNS:Publish", 30 | "SNS:ListSubscriptionsByTopic", 31 | "SNS:GetTopicAttributes", 32 | "SNS:DeleteTopic", 33 | "SNS:AddPermission", 34 | ] 35 | 36 | effect = "Allow" 37 | 38 | principals { 39 | type = "Service" 40 | identifiers = ["events.amazonaws.com"] 41 | } 42 | 43 | resources = [ 44 | "${aws_sns_topic.forwarder_topic.arn}", 45 | ] 46 | 47 | sid = "__default_statement_ID" 48 | } 49 | } 50 | 51 | resource "aws_sns_topic_subscription" "cross_region_sqs_subscription" { 52 | count = var.parent_account ? 0 : 1 53 | topic_arn = aws_sns_topic.forwarder_topic.arn 54 | protocol = "sqs" 55 | raw_message_delivery = true 56 | endpoint = "arn:aws:sqs:${var.central_region}:${data.aws_caller_identity.current.account_id}:${var.central_queue_name}" 57 | } 58 | 59 | resource "null_resource" "sqs_account_subscribe" { 60 | count = var.parent_account ? 1 : 0 61 | provisioner "local-exec" { 62 | command = "aws sns subscribe --topic-arn $SNS_TOPIC_ARN --protocol sqs --notification-endpoint $SQS_QUEUE --attributes RawMessageDelivery=true" 63 | 64 | environment = { 65 | SNS_TOPIC_ARN = aws_sns_topic.forwarder_topic.arn 66 | SQS_QUEUE = "arn:aws:sqs:${var.central_region}:${var.parent_account}:${var.central_queue_name}" 67 | } 68 | } 69 | } 70 | 71 | resource "aws_cloudwatch_event_target" "cwe_rule_target" { 72 | rule = var.cloudwatch_event_rule_id 73 | target_id = "ForwarderTarget${var.cloudwatch_event_rule_id}" 74 | arn = aws_sns_topic.forwarder_topic.arn 75 | } 76 | 77 | -------------------------------------------------------------------------------- /modules/reflex_kms_key/main.tf: -------------------------------------------------------------------------------- 1 | /* 2 | * Reflex KMS Key: Creates a KMS key that will be used by reflex infrastructure for encryption. 3 | * 4 | */ 5 | data "aws_caller_identity" "current" {} 6 | 7 | data "aws_region" "current" {} 8 | 9 | resource "aws_kms_key" "reflex_key" { 10 | description = "Reflex KMS Key" 11 | deletion_window_in_days = 7 12 | enable_key_rotation = true 13 | policy = <